Skip to content

Campaign Tools — MCP Tool Reference

Campaign tools manage the lifecycle of survey campaigns: creating campaigns, assigning interviewers and respondent pools, distributing workload, and sending invitations. Many operations use a batch response pattern with added/skipped/reasons fields.

list_campaigns

List all campaigns, optionally filtered by project, name, or status.

Parameter Type Required Default Description
project_id string No — Filter by project ID
name string No — Filter by campaign name
status string No — Filter by status
limit integer No 100 Maximum results to return

Returns: { items: [...], count, limit }


get_campaign

Get a campaign by ID.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID

Returns: Campaign object or { error }.


create_campaign

Create a new campaign. Campaign mode is automatically determined based on whether interviewers are assigned.

Parameter Type Required Default Description
name string Yes — Campaign display name
project_id string Yes — Parent project ID
questionnaire_id string Yes — Questionnaire to use
status string No "draft" Initial status: draft, active or scheduled. Anything else is refused
admits_volunteers boolean Yes — Whether people never drawn into the campaign's pool may take part (walk-ins, the open link). No default: state it.
offers_ai_channel boolean Yes — Whether respondents may also take the survey in their own AI assistant. No default: state it.
intro_text string When offers_ai_channel is true — Who is collecting the data and why, shown word for word before the first question
consent_text string When offers_ai_channel is true — What the respondent agrees to, shown word for word; a fixed paragraph about the AI provider is always added after it

Returns: Created campaign object.

update_campaign changes status only along the campaign's lifecycle: draft → active or scheduled; scheduled → active or draft; active → paused, completed or aborted; paused → active, completed or aborted; completed or aborted → draft. Any other move, or an unknown status, is refused and nothing is changed.

update_campaign also takes offers_ai_channel, intro_text and consent_text, each optional and kept from the stored campaign when omitted. The texts are checked against the result: turning the assistant on for a campaign that already holds both texts needs no restatement, and blanking a text while the assistant is offered is refused. Turning the assistant off stops every pairing code not yet used; respondents already connected can still finish.


add_interviewers_to_campaign

Add interviewers (Users with interviewer role) to a campaign. Uses the batch response pattern.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
user_ids string[] Yes — List of user IDs to add as interviewers

Returns:

{
  "campaign_id": "...",
  "added": ["uid1"],
  "already_present": ["uid2"],
  "skipped": [{"id": "uid3", "reason": "User not found"}],
  "total_added": 1
}

Skip reasons: "User not found", "No interviewer role".


remove_interviewers_from_campaign

Remove interviewers from a campaign.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
user_ids string[] Yes — List of user IDs to remove

Returns:

{
  "campaign_id": "...",
  "removed": ["uid1"],
  "not_found": ["uid2"],
  "total_removed": 1
}

update_campaign_questionnaire

Change the questionnaire assigned to a campaign.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
questionnaire_id string Yes — New questionnaire UUID

Returns:

{
  "campaign_id": "...",
  "old_questionnaire_id": "...",
  "new_questionnaire_id": "...",
  "success": true,
  "warning": "Campaign has existing surveys — they reference the old questionnaire"
}

Existing Surveys

The warning field appears if the campaign already has surveys. Existing surveys retain references to the old questionnaire.


assign_respondents_to_interviewer

Assign specific respondents to a specific interviewer within a campaign for workload distribution.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
interviewer_id string Yes — Interviewer user UUID
respondent_ids string[] Yes — List of respondent IDs to assign

Returns:

{
  "assigned": ["r1"],
  "already_assigned": ["r2"],
  "not_in_campaign": ["r3"]
}

unassign_respondents_from_interviewer

Remove respondents from an interviewer's assignment. Respondents remain in the campaign pool but become unassigned.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
interviewer_id string Yes — Interviewer user UUID
respondent_ids string[] Yes — List of respondent IDs to unassign

Returns:

{
  "unassigned": ["r1"],
  "not_assigned": ["r2"]
}

get_interviewer_workload

Get the workload for a specific interviewer in a campaign — assigned respondents and survey completion status.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
interviewer_id string Yes — Interviewer user UUID

Returns:

{
  "campaign_id": "...",
  "interviewer_id": "...",
  "assigned_respondents": [
    {"id": "...", "name": "...", "email": "...", "age": 35, "gender": "female", "location": "Berlin"}
  ],
  "survey_status": {
    "respondent_id": {"survey_id": "...", "status": "completed", "completed": true}
  },
  "statistics": {
    "total_assigned": 10,
    "completed": 3,
    "in_progress": 2,
    "not_started": 5,
    "completion_rate": 0.3
  }
}

get_unassigned_respondents

Get respondents in a campaign not yet assigned to any interviewer.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID

Returns:

{
  "campaign_id": "...",
  "unassigned_count": 5,
  "unassigned_respondents": [
    {"id": "...", "name": "...", "email": "...", "age": 28, "gender": "male", "location": "Munich"}
  ]
}

assign_pool_to_campaign

Assign a respondent pool to a campaign. Links the pool so bulk_create_surveys can create surveys for all pool respondents.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
pool_id string Yes — Respondent pool UUID

Returns:

{
  "campaign_id": "...",
  "pool_id": "...",
  "pool_name": "Urban Millennials",
  "respondent_count": 200,
  "previous_pool_id": null
}

get_campaign_pool

Get the respondent pool assigned to a campaign.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID

Returns (pool assigned):

{
  "campaign_id": "...",
  "pool_id": "...",
  "pool_name": "...",
  "pool_description": "...",
  "respondent_count": 200,
  "respondent_ids": ["..."],
  "source_type": "strategy",
  "source_strategy_id": "...",
  "quality_score": 0.95,
  "has_pool": true
}

Returns (no pool): { campaign_id, pool_id: null, has_pool: false, message: "..." }


send_campaign_invitations

Send survey invitation emails to campaign respondents. Sends real emails with magic link access.

Parameter Type Required Default Description
campaign_id string Yes — Campaign UUID
respondent_ids string[] No — Specific respondents to invite
send_to_all_uninvited boolean No false Send to all uninvited respondents

Real Emails

This tool sends actual emails. You must specify either respondent_ids or send_to_all_uninvited=true.

Returns:

{
  "campaign_id": "...",
  "results": {
    "sent": [{"respondent_id": "...", "name": "...", "email": "...", "survey_id": "...", "survey_url": "..."}],
    "skipped": [{"respondent_id": "...", "name": "...", "reason": "No email address"}],
    "failed": [{"respondent_id": "...", "name": "...", "reason": "Email send failed: ..."}]
  },
  "summary": {"sent": 8, "skipped": 1, "failed": 1}
}

When the campaign offers the AI assistant, every invitation email adds a paragraph pointing the respondent to the AI option on their survey page, and each sent entry also carries connector_url, the respondent connector address.

Eligibility requirements per respondent:

  • Must have an email address
  • notification_preference must not be "none"
  • Survey must not be already completed or in progress
  • The campaign must accept them. Inviting somebody it never drew into its pool needs a campaign that admits volunteers and is actively fielding — the same gate a walk-in meets. Their survey is then recorded as a volunteer rather than as a drawn case, so the dataset can still tell the two apart.

Skip reasons: "Respondent not found", "No email address", "Opted out of notifications", "Survey already completed", "Survey already in_progress", plus a refusal naming whichever half of the admission gate said no — the campaign does not take walk-ins, or it is not currently being fielded.


Chain Tools

Two of the sequences above are also available as a single call. They are not shortcuts that skip anything: each step is the same tool documented on this page, so the same records are written, the same audit events land, and any refusal a raw tool would give you is given here unchanged — plus stopped_at_step, naming which step a stop came from. Nothing is undone, so a chain that stops partway leaves what the earlier steps built and you continue with the individual tools.

A chain checks your entitlement for every step before it builds anything. If your subscription does not cover a later step you are refused up front rather than handed a half-built fielding.

prepare_campaign_fielding

Run a campaign's whole fielding chain: create the sampling strategy, generate the respondent pool, assign the pool to the campaign, publish (and pin) the questionnaire version the campaign will field, and create the surveys.

Parameter Type Required Default Description
campaign_id string Yes — Campaign to prepare. It must already name the questionnaire you intend to field
admits_volunteers boolean Yes none This fielding's volunteer-admission policy, stated explicitly. There is no default, and it must match the campaign's own setting — change that with update_campaign first if it is wrong
strategy_name string Yes — Name for the sampling strategy this run creates
target_size integer Yes — The pool draw's requested size: respondents to draw, before oversampling (> 0). Passed to the draw as requested_size and stored on the pool
factors object[] No [] Demographic distribution factors the strategy grades against
selection_algorithm string No "greedy" Selection algorithm for the pool draw ("greedy" or "random_constrained")
oversample_factor number No 1.0 Oversampling multiplier for the pool draw (≥ 1)
quality_metric string No "rmse" Metric the strategy grades fit with
require_email boolean No false The pool draw admits only respondents that have an email address
pool_name string No — Name for the generated pool; defaults to a label derived from the strategy

The strategy this run creates holds only factors and quality_metric. The draw settings (target_size, oversample_factor, selection_algorithm, require_email) go to the pool step and are stored on the pool, exactly as a direct generate_pool_from_strategy call would store them.

Returns: ready_to_invite, the strategy_id, pool_id, pool_respondent_count, qml_version_id and surveys_created / surveys_skipped it produced, plus next_step. On a stop: stopped_at_step with the refusing step's own error and detail, and whatever earlier steps had already built.

It stops before the invitations go out

The chain never calls send_campaign_invitations. Recruitment is the one irreversible, externally visible act in the sequence — mail cannot be unsent — so it stays a separate decision you make after reading what the chain built.

Why the publish step matters. A hand-built chain routinely forgets to publish, and an unpinned campaign resolves the questionnaire's head at read time — so an edit made mid-fielding silently changes the instrument under respondents who have already answered.

Re-running draws again. Each run creates a new strategy and pool and rebinds the campaign to it (a pool is single-use), then fans out over the new pool, skipping respondents that already hold a survey for this campaign. Against a roster larger than target_size a second run therefore adds newly drawn respondents rather than replacing anyone.


simulate_campaign_responses

Create the campaign's surveys and then generate synthetic responses over them. See mass_fill_surveys for what each distribution mode and persona profile does.

Parameter Type Required Default Description
campaign_id string Yes — Campaign whose surveys are created and then filled
distribution string No "realistic" "realistic", "random", "stratified", or "llm"
max_surveys integer No — Cap on how many of the campaign's surveys are filled
profiles string[] No ["random"] Persona names cycled over respondents
concurrency integer No 1 Parallelism for the fill's inner loop

Returns: surveys_created / surveys_skipped, and a task_id for the fill with status: "pending". Poll it with get_task_status exactly as you would a mass_fill_surveys run. On a stop: stopped_at_step with the refusing step's error and detail, and task_id: null.


Error Handling

Common Errors

Scenario Response
Campaign not found {"error": "Campaign {id} not found"}
Interviewer not in campaign {"error": "Interviewer {id} is not assigned to this campaign"}
No questionnaire/pool {"error": "Campaign has no questionnaire assigned"}

Batch Operation Pattern

Most campaign tools that accept lists of IDs return structured results rather than failing on individual items:

{
  "added": ["id1"],
  "already_present": ["id2"],
  "skipped": [{"id": "id3", "reason": "User not found"}],
  "total_added": 1
}

This pattern applies to: add_interviewers_to_campaign, remove_interviewers_from_campaign, assign_respondents_to_interviewer, unassign_respondents_from_interviewer, and send_campaign_invitations.

Each skipped/failed item includes a reason field explaining why it was excluded from the operation.