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¶
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):
- Point the client at your tenant (
export ASKALOT_MCP_URL=https://portor.<tenant>.askalot.io/mcp). - Trigger any MCP tool. The client opens your browser to Portor's
/authorizeendpoint, which redirects to the platform OIDC login. - Sign in and approve the consent screen. The client exchanges the code for tokens and retries the original call.
- 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:
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_respondentscaps 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_endsandderive_silverare MCP-only. There is no REST equivalent to look for. - Datasets are not MCP-only.
list_datasets,get_dataset,delete_datasetandexport_datasetall 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, andget_qml_contentabove 32 KB, return a short-lived signeddownload_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_statusfor a task id, orget_datasetfor 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:
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=analystshorthand.
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_fieldingstops before the invitations go out. It never sends; that stays a separate, deliberatesend_campaign_invitationscall 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_pipelinereports a stop in its{success: false, error, detail}envelope; the other two return their normal result witherroranddetailfilled in and nosuccesskey. - 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
interviewerrole 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
interviewerrole 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:
- Campaign Tools — batch patterns (added/skipped/reasons), invitation eligibility
- Survey Tools — state machine transitions, flow errors
- Dataset Tools — stage validation, format errors
- Pool Tools — duplicate handling, strategy validation
- Sampling Tools — factor schema validation, default strategy protection
- Questionnaire Tools — lifecycle constraints, active campaign checks
- Respondent Tools — bulk creation errors
Next Steps¶
-
REST API
Traditional REST endpoints for integration
-
QML Syntax
Learn the questionnaire markup language
-
Campaigns
Configure and manage survey campaigns