Skip to content

MCP Interface

Askalot supports the Model Context Protocol (MCP)—an open standard that enables AI assistants to interact directly with the platform. This allows AI agents to create surveys, manage campaigns, execute questionnaire flows, and analyze results through natural language conversations.

What is MCP?

MCP is a standardized protocol that lets AI systems like Claude interact with external tools and data sources. With Askalot's MCP interface, you can:

  • Automate survey workflows - Create projects, questionnaires, and campaigns through AI conversations
  • Execute surveys programmatically - Navigate questionnaire flows and submit responses
  • Manage respondents - Import, organize, and assign survey targets to campaigns
  • Query audit trail - Track changes and ensure compliance
  • Access documentation - AI agents can read platform documentation for context

Connecting to the MCP Interface

Endpoint

https://portor.<tenant>.askalot.io/mcp

Replace <tenant> with your organization's tenant name.

This is the connector for you and your team. Respondents never connect here: a Campaign that offers the AI assistant gives each respondent a separate connector, https://portor.<tenant>.askalot.io/respond/mcp, reached with a pairing code from their survey page. It holds only their own survey. See AI-Assisted Self-Interview.

Authentication

The MCP interface authenticates with OAuth 2.1, not a pasted API token. On the first tool call, Portor returns 401 with an OAuth discovery hint and your MCP client (for example, the Askalot plugin for Claude Code) walks you through a browser sign-in against the platform OIDC provider. The client then stores the resulting access + refresh tokens and sends the access token as Authorization: Bearer on every call — you never handle the bearer value yourself.

Configuring the endpoint (URL only):

The only thing you configure is the server URL. The Askalot plugin reads it from the ASKALOT_MCP_URL environment variable; a plain .mcp.json entry carries the same URL and nothing else — no token field:

{
  "mcpServers": {
    "askalot": {
      "type": "http",
      "url": "https://portor.<tenant>.askalot.io/mcp"
    }
  }
}

Signing in (first use):

  1. Point the client at your tenant (export ASKALOT_MCP_URL=https://portor.<tenant>.askalot.io/mcp).
  2. Trigger any MCP tool. The client opens your browser to Portor's /authorize endpoint, which redirects to the platform OIDC login.
  3. Sign in and approve the consent screen. The client exchanges the code for tokens and retries the original call.
  4. The browser handshake happens once; the refresh token is exchanged transparently afterward.

REST/CI uses a token instead

The browser OAuth flow is for interactive MCP clients. For non-interactive REST or CI use, generate a classic API token in Roundtable → Profile Settings and send it in the X-Api-Token header on the REST API. That token authenticates REST only — it is not the credential for the MCP transport, which resolves OAuth (or synthetic service) bearer tokens exclusively.

Your Active Organization

Your MCP session is always in exactly one organization at a time — its active organization — and every tool call resolves against it. Projects, questionnaires, campaigns, respondents and datasets are all organization-scoped, so this is the single most important thing to know about where a tool is looking.

Where you start. A fresh session lands in your home organization, the one your account belongs to. Nothing asks you to choose during sign-in.

Seeing it. list_organizations returns every organization the session may act in, marks which one is active, and flags any that are sealed. The list is exactly what the Switch Organization menu offers you in the browser — if you belong to one organization, that is the whole list and there is nothing to manage.

Changing it. switch_organization moves the session to another organization from that list. Every later call resolves in the new one, and the change persists: restart your MCP client and you resume where you left off. The tenant is fixed when you sign in and never changes, so switching only ever moves you between organizations inside one tenant.

Being able to enter versus being able to act. The list holds the organizations you are a member of — memberships an administrator grants and removes, never the session itself (an Askalot operator's list is every organization in the tenant). Membership lets the session move in; which tools it can use there comes from your roles in that organization, and a role you hold elsewhere does not carry over. In an organization where you are a member but hold no role, the tool list shrinks to the session tools — list_organizations, switch_organization, list_toolsets, get_my_entitlements and your model settings — until an administrator grants you a role there, so you can always switch back out. If an administrator removes you from the organization the session is in, the connection ends; sign in again and the new session starts in your home organization.

A not-found tells you where it looked

Because reads are filtered by the active organization, an entity that lives elsewhere is simply absent — which reads exactly like an entity that never existed. Askalot names the organization it searched:

Project 6f2a… not found (searched organization: ACME Corp)

That is the signal to call list_organizations and switch, rather than concluding the project does not exist. The message names only the organization that was searched, never the one that owns the entity.

Sealed Organizations

Some organizations are sealed — a curated demo, or a former customer's data kept available for its retention window. list_organizations marks them, so you can tell before you act.

What is sealed is that organization's research record — its projects, questionnaires, campaigns, respondents, pools, surveys, datasets and Research Papers. Reading is untouched: listing and fetching all of those keeps working, as do the documentation and methodology-library tools. The audit tools keep their usual rules (see Audit & Compliance). Creating, updating and deleting them is refused with org_sealed. Each tool declares whether it reads or writes, and that declaration is what the refusal is driven by — so the boundary is a property of the tool, not a guess about its name.

Trying an instrument out still works, because a preview changes nothing in the record. create_survey given no campaign starts a preview rather than a survey, and get_survey_current_item, submit_survey_response and finish_survey will walk it — so you can review a questionnaire end to end in a sealed organization. Give create_survey a campaign and it is refused: that walk would be a real case, and real cases enter the dataset.

list_organizations and switch_organization both keep working there, so a session can always see where else it may go and leave.

Sealing also disables API tokens for that organization: you cannot generate one, and an existing one stops working. The OAuth sign-in above is unaffected.

Your AI Model Settings

Askalot runs three kinds of AI call on your behalf, and each picks its provider and model separately: embedding (fixed to the platform's local model — index and search must use the same one, so it is not configurable), direct LLM (a single raw completion: keyword extraction on a document search, and the extraction pass when a document is ingested) and reasoning, which runs the agents and carries a high / medium / low tier.

get_model_configuration reports what is in effect for your own account and, per slot, where the value came from:

Source Meaning
user you chose it
org inherited from your organization
platform Askalot's default (the embedding slot only)
mixed the provider and the model came from different places
unconfigured nothing is set, and there is no platform default

The direct-LLM slot and every reasoning tier read unconfigured until you or your organization choose a provider, and it is the answer to a common confusion: those slots have no platform default on purpose, because the calls are billed to your own provider account. A document search or an ingest that refuses with "requires an LLM provider — none is configured", or an agent that asks you to set up a provider, is this state, not a broken key. An unconfigured reasoning tier reads null in effective_config.

update_model_configuration changes your own slots. It merges: name direct_llm and your reasoning tiers stay as they were, name reasoning: {"high": …} and medium and low are untouched. Pass null for a tier — or clear_direct_llm: true — to hand a slot back to your organization's value. The three reasoning tiers must end up on one provider, and an unset tier counts as a mismatch, so a first write names all three: setting high alone is refused, because medium and low would be left unset. For the same reason, clearing reasoning when your organization sets none means passing null for all three tiers at once.

The providers are not interchangeable between slots, because the transports differ: direct_llm takes anthropic_api or bedrock (a Claude subscription token cannot sign a raw completion), while reasoning also takes anthropic_subscription. Ask get_model_configuration for the accepted values and the default model per provider rather than guessing; a refused write changes nothing and names what was wrong.

Both tools act on your account only. The same settings are on the AI Models section of your profile page, and a change through either door is recorded in the audit trail.

Choosing MCP or REST

The MCP server is one of two surfaces over the same capabilities, and it cannot tell you when you picked the wrong one: the wrong lane is slow and context-expensive, not an error.

  • MCP is for one semantic action, a query, or anything that needs the server's judgement. Most work belongs here.
  • REST (https://portor.<tenant>.askalot.io/api/v1) is for bulk work and anything whose payload or result is large. Loop and filter outside your context, and keep only a summary.

Which credential, and it depends on who is calling. The OAuth access token that authenticates your MCP session is accepted on /api/v1 as Authorization: Bearer <token>. A Personal Access Token goes in the X-Api-Token header instead, never as Bearer; see Authentication.

  • A program that holds its own token — an SDK agent managing its OAuth session — may reuse it on REST. It already has the credential in hand, and one session is one identity on both lanes.
  • Code you generate for a sandbox, or anything that outlives the turn, uses a PAT. Not a preference: a code-server sandbox holds no credential that reaches this API. It has no service token by design, its IPC token authenticates the other direction, and its access token is public and rotates when you switch projects. A PAT is also separately revocable, which a session token is not.
  • A model driving a chat client never sees the token at all — the client holds it. If you are writing code, you are in the case above.

Facts that are easy to get backwards:

  • bulk_create_respondents caps at 100 per call on both lanes. REST validates the same input, so 500 respondents is five calls either way. REST wins on round trips and context, never on batch size.
  • The pipeline-job verbs code_open_ends and derive_silver are MCP-only. There is no REST equivalent to look for.
  • Datasets are not MCP-only. list_datasets, get_dataset, delete_dataset and export_dataset all have routes under /api/v1/datasets, and listing or exporting is exactly the large-result work REST is for.
  • Large content comes back as a link. export_dataset, and get_qml_content above 32 KB, return a short-lived signed download_url; fetch it over plain HTTPS.
  • Poll rather than loop. Without a code sandbox, do not hand-write hundreds of sequential tool calls: start the job and poll get_task_status for a task id, or get_dataset for a dataset stage.

Narrowing the Tools a Connection Sees

The full surface is over 120 tools, which is more than any single piece of research needs in front of it. A connection can name the areas it wants, in the URL:

https://portor.<tenant>.askalot.io/mcp?toolsets=analysis,campaign

The values are comma-separated toolset names. A connection that names none gets everything, which is the default and stays the default.

Nine toolsets can be filtered this way:

Toolset What it holds
project Projects and their ownership
questionnaire Questionnaires and the QML lifecycle
sampling Respondents, pools and sampling strategies
campaign Campaigns and their configuration
fielding Surveys, invitations, interviewers, the response flow
analysis Bundles, datasets and the Bronze/Silver/Gold pipeline
documents Your uploaded reference documents
methodology The shared survey-methodology library
audit The audit trail

Eight more are admitted on every connection whatever you name, because taking them away would leave an agent unable to correct its own course: your session and organization tools, conversation history, entitlements, documentation, the list_toolsets tool itself, the research paper, the composite tools below, and get_task_status — so a composite you start on a narrowed connection can always be polled from it.

Two things this is not, both worth being clear about:

  • It is not a permission boundary. You choose your own URL, so naming a toolset keeps nobody out of anything. What you are allowed to do is decided by your subscription tier, your role, and project ownership — none of which this changes. It narrows what is in front of you, not what you may do.
  • It is not a role. The parameter takes these toolset names and nothing else; there is no ?toolsets=analyst shorthand.

Call list_toolsets at any time to see every toolset, which tools each holds, what your connection named, and any names it did not recognise. If you call a tool your connection scoped out, the refusal names the toolset that holds it and the URL parameter that would admit it — so a wrong guess is recoverable without reading this page.

Changing the scope means reconnecting, since it lives in the connection URL. This is an HTTP-transport feature; a stdio connection has no URL and is always unscoped.

Doing a Whole Sequence in One Call

Three common sequences are each available as a single tool. They are not shortcuts that skip steps — every step is the same tool you would call by hand, writing the same records and leaving the same audit trail:

One call What it runs
prepare_campaign_fielding Sampling strategy → respondent pool → assign the pool → publish the questionnaire version → create the surveys
simulate_campaign_responses Create the surveys → generate synthetic responses over them
run_bundle_pipeline Bronze extraction → Silver (coding + weighting) → Gold

Three things to know before using them:

  • prepare_campaign_fielding stops before the invitations go out. It never sends; that stays a separate, deliberate send_campaign_invitations call you make after reviewing what the chain built. Email cannot be unsent.
  • If a chain stops partway, what it already built stays. Nothing is undone. The response names the step in stopped_at_step; a non-null value means the chain did not finish, and you continue from there with the individual tools. run_bundle_pipeline reports a stop in its {success: false, error, detail} envelope; the other two return their normal result with error and detail filled in and no success key.
  • A chain checks the whole sequence up front. If your subscription does not cover a later step, you are told so before anything is built, rather than being handed a half-finished result.

simulate_campaign_responses and run_bundle_pipeline hand back a task id to poll with get_task_status. run_bundle_pipeline reports the step its run reached and, once finished, each stage's dataset; simulate_campaign_responses hands back the same fill task the raw mass_fill_surveys tool would, so its poll reports progress rather than steps.

Available Tools

The MCP interface provides tools organized by functionality.

Session and Organizations

Tool Description
list_organizations List the organizations this session may switch into, marking the active one and flagging sealed ones
switch_organization Move the session's active organization — every later call resolves against it

Project Management

Tool Description
list_projects List all your projects
get_project Get project details
create_project Create a new project
add_project_owners Add users as project owners (targets must be members of the project's organization)
remove_project_owners Remove users from project ownership (same membership rule applies)

Questionnaire Operations

Tool Description
list_questionnaires List all questionnaires
get_questionnaire Get questionnaire details
create_questionnaire Create a new questionnaire
rename_questionnaire Rename a questionnaire (address unchanged)
delete_questionnaire Soft-delete a questionnaire
restore_questionnaire Restore a recently soft-deleted questionnaire
list_qml_files List the organization's QML questionnaires
inspect_qml_file Quick summary of a questionnaire's current version
preview_qml_item Show one questionnaire item as a respondent sees it; a host that renders MCP Apps shows its real controls in a read-only panel
get_qml_content Retrieve a questionnaire's current QML (returns a short-lived download link instead of inline content above 32 KB)
validate_qml_file Run Z3 SMT validation on a questionnaire's current version (reachability, consistency)
qml_quality_report Compute the D2–D8 design-quality scorecard for a questionnaire's current version
save_qml_file Save QML as a questionnaire's new version — never blocks on broken drafts (content over 32 KB is refused; use the upload handle below)
request_upload_url / finalize_upload Two-step handle for saving QML content over save_qml_file's 32 KB limit
publish_qml_file Publish a questionnaire to a campaign — formally validates and pins the exact version (the only gated door)
move_qml_to_project Move a questionnaire to another project (version history preserved)
unpublish_qml_file Soft-delete a questionnaire, retaining its version history (blocked if active campaigns)

Parameter Reference — parameters, return schemas, QML lifecycle, Z3 output structure

Document Discovery

Tool Description
list_indexed_documents List all documents indexed for AI semantic search
get_document_summary Get the stored Markdown summary for a specific document
get_document_chunk Retrieve a specific chunk of an indexed document by index
search_document_chunks_by_keyword Search indexed document chunks by keyword

These two searches cost you nothing and need no setup

get_document_chunk and search_document_chunks_by_keyword match on meaning alone — no AI model is involved — so they work whether or not you have configured a provider, and they never appear on your provider bill.

The knowledge-graph searches are different: search_methodology_library and get_methodology_paper_summary read your question with a model before they can answer. Those run on your own Direct LLM slot, so their (small) cost lands on your provider account, and without a configured slot they return an actionable error. See AI Functions & Model Options.

Research Paper

Tool Description
read_paper Read a project's Research Paper — the assembled chapters, plus each unit's current edit token
edit_paper_unit Replace an exact passage inside one unit of the paper, leaving everything else byte-for-byte unchanged

You cannot edit a chapter you have not just read

There is deliberately no "write the whole chapter" tool. read_paper returns a base_hash per unit, and edit_paper_unit refuses an edit whose base_hash is missing (you never read it) or stale (someone else changed the unit since you did). This is what stops an agent from silently overwriting a researcher's approved text, so read first, every turn — a hash from earlier context is not a read.

An edit names an old_string to find and a new_string to put in its place. If the old_string appears more than once the edit is refused with the match positions rather than guessing which one you meant. On success you get a fresh new_base_hash, so several edits to the same unit in one turn need no re-read in between.

Content is HTML restricted to a small allowlist of tags and classes. Anything outside it is refused, and the refusal names each disallowed construct so you can correct it.

Campaign Management

Tool Description
list_campaigns List campaigns (optionally by project)
get_campaign Get campaign details
create_campaign Create a new campaign
update_campaign Update campaign fields (name, dates, status, volunteer admission, the AI assistant offer and its texts)
delete_campaign Delete a campaign
add_interviewers_to_campaign Add interviewers (Users with interviewer role)
remove_interviewers_from_campaign Remove interviewers from a campaign
update_campaign_questionnaire Change the questionnaire assigned to a campaign
assign_pool_to_campaign Assign a respondent pool to a campaign
get_campaign_pool Get the respondent pool assigned to a campaign
send_campaign_invitations Send survey invitation emails to respondents

Respondents are not added to a campaign directly — assign a respondent pool with assign_pool_to_campaign, then bulk_create_surveys creates a survey per pool respondent.

Parameter Reference — parameters, return schemas, batch patterns, invitation eligibility

Respondent Management

Tool Description
list_respondents List all respondents (survey targets)
get_respondent Get respondent details
create_respondent Create a new respondent
update_respondent Update respondent information
delete_respondent Remove a respondent
bulk_create_respondents Create multiple respondents at once
bulk_delete_respondents Delete respondents by filter (two-step dry_run workflow)
generate_survey_access_token Mint a magic-link token for a respondent's survey
send_survey_invitation Email a respondent a magic-link survey invitation

Parameter Reference — parameters, return schemas, bulk creation, demographics

Respondent Pools

Respondent pools let you organize respondents into reusable groups for campaigns.

Tool Description
list_respondent_pools List all respondent pools
get_respondent_pool Get pool details including respondent list
create_respondent_pool Create a new respondent pool
add_respondents_to_pool Add respondents to an existing pool
remove_respondents_from_pool Remove respondents from a pool
generate_pool_from_strategy Draw a pool from a sampling strategy at a requested size; the pool stores its draw settings
preview_pool_generation Preview a draw without creating the pool
refresh_pool_from_strategy Redraw an existing pool with the draw settings it stores
delete_respondent_pool Delete a respondent pool

Parameter Reference — parameters, return schemas, duplicate handling, strategy generation

Sampling Strategies

Sampling strategies define target demographic distributions for creating representative respondent samples. A strategy carries no size or selection settings — those are inputs to each pool draw — so one strategy can feed pools of any size.

Tool Description
list_sampling_strategies List all sampling strategies
get_sampling_strategy Get strategy details including factors
create_sampling_strategy Create a strategy with custom demographic factors
create_default_strategy Create a strategy with standard gender and age factors
update_sampling_strategy Update an existing strategy
advance_sampling_strategy Clone a strategy and append reality-grounded outcome factors
delete_sampling_strategy Delete a sampling strategy

Example: Creating a Custom Sampling Strategy

AI Assistant: I'll create a sampling strategy targeting your demographics.

→ create_sampling_strategy(
    name="Urban Millennials",
    project_id="...",
    factors=[
      {"name": "Gender", "attribute_path": "gender",
       "target_distribution": {"male": 0.48, "female": 0.50, "other": 0.02}},
      {"name": "Age Group", "attribute_path": "age",
       "target_distribution": {"25-34": 0.60, "35-44": 0.40}}
    ]
  )

Strategy created! Now generating a representative pool...
→ generate_pool_from_strategy(strategy_id, pool_name="Urban Millennials Pool",
                              requested_size=200)

Parameter Reference — parameters, return schemas, factor schema, distribution validation

User Management

Tool Description
list_users List users in your active organization (including interviewers) — call switch_organization to see another organization's users
get_user Get user details

Note: Interviewers are Users with the interviewer role. They are managed as Users, not as Respondents.

Interviewer Workload

For campaigns with interviewers (Users with interviewer role), manage workload distribution:

Tool Description
assign_respondents_to_interviewer Assign respondents to a specific interviewer (User)
unassign_respondents_from_interviewer Remove interviewer assignments
get_interviewer_workload View assigned respondents and completion status
get_unassigned_respondents Find respondents not yet assigned

Survey Execution

Tool Description
list_surveys List surveys (filter by campaign or respondent)
create_survey Start a new survey session
update_survey Update a survey's status and flow state (bound entities are immutable)
bulk_create_surveys Create surveys for all respondents in a campaign
bulk_delete_surveys Delete surveys by filter (two-step dry_run workflow)
get_survey_current_item Get the current question with its options folded in
submit_survey_response Submit an answer and advance (next step folded in)
finish_survey Explicitly complete a survey
mass_fill_surveys Enqueue an asynchronous background task to fill surveys with synthetic test responses (returns {task_id, status})
get_task_status Poll an async task by task_id: a mass_fill_surveys run, or a simulate_campaign_responses or run_bundle_pipeline chain (dataset coding jobs are polled via get_dataset instead)

Parameter Reference — parameters, return schemas, state machine, distribution modes, personas

Data Analysis

A Bundle binds a Project, Questionnaire, and Campaign subset to one linear Bronze → Silver → Gold dataset chain. Bundle tools manage that binding and carry the Bundle's quality story; dataset tools run each Bundle-scoped stage — extraction, coding + weighting, refinement, and export.

Tool Description
create_bundle Create a pipeline Bundle
list_bundles List Bundles, optionally filtered to one project
get_bundle Bundle detail, including its stage recipes (Silver Calibration Targets, Gold operations)
clone_bundle Deep-copy a Bundle's ready datasets + recipes into an independent variant
delete_bundle Delete a Bundle and its dataset chain
get_bundle_quality The Bundle's Representativeness story (selection, fielded, weighted, fielding shift, response)
compare_bundle_quality Bronze vs Silver improvement against the Bundle's current Strategy
get_bundle_coding Open-end coding state — the candidates the Bronze offers, the analyst's answer kind for each (descriptive, nominal or identifier; absent = unclassified), the question selection, and per question the proposed dimensions, their categories and which are selected
set_coding_selection Set which open-text units the Bundle codes (manager role, identified user required); refuses an unclassified or identifier unit, and no tool classifies one
assign_bundle_strategy Assign (or clear) the Bundle's current Sampling Strategy
set_calibration_targets Edit the Bundle's Calibration Targets — the Silver weighting spec
list_datasets List datasets scoped to a Bundle's chain
get_dataset Get dataset details, metrics and status — the bounded surface pipeline steps say to poll
get_dataset_schema Get a dataset's column schema, or just its column names and control types, optionally narrowed to named columns
create_bronze_dataset Extract a Bundle's Bronze dataset
code_open_ends (alias derive_silver) Code open-ended text, then weight against Calibration Targets, producing the Bundle's Silver
create_gold_dataset Refine a Bundle's Silver into its Gold, using the Gold operation catalog
export_dataset Export a ready dataset (CSV, XLSX, SPSS, Parquet, R, JSON)
delete_dataset Delete a dataset

Bundle Tools Reference — Bundle lifecycle and quality parameters, return schemas

Dataset Tools Reference — stage parameters, return schemas, export formats

Audit & Compliance

Tool Description
query_audit_events Search audit trail with filters
get_entity_history View complete history of any entity
get_audit_stats Event counts by entity type

All three read only your tenant and your active organization. To read another organization's trail, switch into it first. Events recorded without an organization, such as system and service actions, are not returned.

Within the organization, what you see depends on your role:

Who What the audit tools return
Administrator (org_admin of the organization, or an Askalot operator) The whole organization's trail, including get_audit_stats
manager Your own actions, plus the full history of projects, questionnaires, campaigns and surveys in projects you own
Anyone else Your own actions only

Asking for another user's actions, or for the history of an entity outside your reach, is refused. get_audit_stats is administrator-only, and a connection acting for no user reads no audit at all. In an organization where every member reads every project, such as the public Askalot Demo organization, the audit trail is administrator-only: reading the research does not extend to reading who did what, because audit events carry details such as IP addresses.

The audit viewer in the browser applies the same roles, but an Askalot operator and a regular user reach further there:

Who What the browser audit viewer shows
Askalot operator (tenant_admin) Every event in the tenant, across all organizations
Organization administrator (org_admin) Every event of the active organization
manager Within the active organization: your own actions, plus the history of projects, questionnaires, campaigns and surveys in projects you own
Anyone else Your own actions only, including your sign-ins

A sign-in is recorded in the organization the session starts in, and a sign-out or password change in the organization the session is working in, so they are part of that organization's trail like any other action.

Events without an organization

Some events are recorded without an organization: system and service activity, and a sign-in attempt that was refused. They belong to no organization's trail, so an organization administrator or a manager does not see them, in the browser or through these tools. They appear in the browser's tenant-wide view, and a regular user still sees their own there. The audit viewer says so to an organization administrator.

Documentation Access

Tool Description
list_documentation List available documentation
get_documentation Load a specific document
search_documentation Search documentation by keyword

MCP Resources

Resources provide read-only access to platform data:

Resource URI Description
repository://overview Platform statistics and counts
repository://schema Entity relationships and structure
qml://files The organization's QML questionnaires
docs://primer Platform overview
docs://qml-syntax QML language reference
docs://getting-started Quick start guide

Additional documentation resources mirror the knowledge base (docs://guide, docs://glossary, docs://campaign-management, docs://survey-execution, docs://creating-surveys, docs://data-analysis, docs://quality-metrics).

Example Workflows

Creating a Survey Campaign with AI

AI Assistant: I'll help you set up a customer satisfaction survey.

1. First, let me create a project...
   → create_project("Customer Research 2026", "Annual satisfaction study")

2. Now I'll register your questionnaire...
   → create_questionnaire("CSAT Survey", qml_content, project_id)

3. Creating the campaign...
   → create_campaign("Q1 CSAT", project_id, questionnaire_id, offers_ai_channel=false)

4. Adding your respondent list...
   → bulk_create_respondents(100, generate_demographics=true)
   → create_respondent_pool("CSAT Sample", respondent_ids=[...])
   → assign_pool_to_campaign(campaign_id, pool_id)
   → bulk_create_surveys(campaign_id)

Your campaign is ready! 100 respondents have surveys waiting.

Creating a Representative Sample

AI Assistant: I'll create a demographically balanced sample for your study.

1. Creating a sampling strategy with your target demographics...
   → create_sampling_strategy(
       name="National Representative",
       project_id,
       factors=[
         {"name": "Gender", "attribute_path": "gender",
          "target_distribution": {"male": 0.49, "female": 0.51}},
         {"name": "Age", "attribute_path": "age",
          "target_distribution": {"18-34": 0.30, "35-54": 0.40, "55+": 0.30}}
       ]
     )

2. Generating a pool that matches your targets...
   → generate_pool_from_strategy(strategy_id, pool_name="Q1 Sample Pool",
                                 requested_size=500)

3. Assigning the pool to your campaign...
   → assign_pool_to_campaign(campaign_id, pool_id)

Done! Your campaign now has 500 respondents balanced to match
national demographics.

Executing a Survey Programmatically

AI Assistant: I'll walk through the survey and show you each question.

→ create_survey(questionnaire_id, respondent_id)
→ get_survey_current_item(survey_id)

Question 1: "What is your age?"
Type: Integer input (18-100)

User: 35

→ submit_survey_response(survey_id, "q_age", 35)
→ get_survey_current_item(survey_id)

Question 2: "How satisfied are you with our service?"
Type: Scale (1-5)
...

Managing Interviewer Workload

AI Assistant: Let me distribute the 50 respondents among your 5 interviewers (Users with interviewer role).

→ get_unassigned_respondents(campaign_id)
   Found 50 unassigned respondents

→ assign_respondents_to_interviewer(campaign_id, interviewer_user_1, respondents[0:10])
→ assign_respondents_to_interviewer(campaign_id, interviewer_user_2, respondents[10:20])
...

→ get_interviewer_workload(campaign_id, interviewer_user_1)
   Assigned: 10 respondents
   Completed: 0
   In Progress: 0

Platform Concepts

Understanding the entity model helps when working with the MCP interface:

Entity Hierarchy

Project
├── Questionnaire (owns a QML version chain)
└── Campaign
    ├── Respondents (survey targets)
    ├── Interviewers (Users with interviewer role, optional)
    └── Surveys (individual sessions)

Every questionnaire belongs to a project, and every user has a default project (is_default: true). When a request names no project, find the default with list_projects and use its id.

Respondents and Interviewers

  • Respondents: Survey targets with demographic data. Their survey completions determine campaign progress. They access surveys via magic links (no platform authentication required).
  • Interviewers (optional): Users with the interviewer role who facilitate surveys for respondents. They have platform login and help respondents complete surveys but don't fill in surveys themselves.

Interviewers can be assigned to help respondents in person or by phone. When interviewers are assigned, surveys track which interviewer facilitated each respondent's survey.

Respondents vs Users

  • Respondents are survey targets with demographic data (age, gender, location). They don't have platform accounts.
  • Users are platform accounts for authentication and management. Users with the interviewer role can facilitate surveys.
  • Respondents and Users are separate entities. Interviewers are Users, not Respondents.

Error Handling

MCP tools return structured error messages. Two apply across every category:

  • not_found — the entity is not in the session's active organization. The message names the organization it searched; see Your Active Organization.
  • org_sealed — the active organization is sealed. Reads succeed; this refusal only ever comes from a tool that would have changed its research record.

Detailed error patterns are documented per category:

Next Steps

  • REST API


    Traditional REST endpoints for integration

    REST Docs

  • QML Syntax


    Learn the questionnaire markup language

    QML Guide

  • Campaigns


    Configure and manage survey campaigns

    Campaign Guide