Agent API and MCP
Usercall API
An agent can talk to customers and learn why. These operations are the Agent API. MCP tools call the same paths.
Talk to customers and learn why
An agent can ask a research question, hear it from a participant, and read back themes, quotes, and risks. The Agent API and the hosted MCP server are the same operations.
When to call it
Start here, then authenticate, then follow the workflow once before calling individual operations.
Authentication
Machine clients send a personal-account API key. Claude, ChatGPT, and Cursor use the hosted connector and OAuth instead of a key.
When to call it
Send the key on every Agent API call. Use the connector when a person is connecting an assistant.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | header | API | Bearer token from Home → Developer. Shown once at creation. Scope is the personal account only. |
| Connector | URL | MCP | https://mcp.usercall.co. OAuth signs the person in. No API key for the hosted connector. |
Response
401 { "error": "unauthorized", "message": "Unauthorized" }
Token revoked, expired, or not valid for the personal account uses the same error with a specific message.
500 { "error": "auth_lookup_failed", "message": "Failed to validate API token" }
Rate limit is 60 requests per minute per token:
429 { "error": "rate_limited", "message": "Rate limit exceeded", "limit": 60, "remaining": 0, "reset_at": "..." }Example
curl -s "$BASE_URL/api/v1/agent/studies" \
-H "Authorization: Bearer $USERCALL_API_KEY"One workflow
Go from a product problem to evidence: create the study, simulate or review the guide, share the participant link or leave a trigger paused, then read results.
When to call it
Follow this order. Do not treat an in-progress result as a finding.
Example
1. You already know what happened in the product, not why.
2. POST /api/v1/agent/studies with key_research_goal only.
3. POST .../simulations, then POST .../reviews.
4. PATCH workflow_questions from a recommendation when you want to change the guide.
5. Send interview_link, or POST /api/v1/agent/triggers. The trigger stays paused until a human opens activation_url.
6. GET the study until status is complete.
7. GET .../results. Use format=full only when you need transcripts.Create a study
/api/v1/agent/studies
Creates a study from the research question and returns the participant link.
When to call it
Call this first. The next step is simulate_interview, before anyone real sees the link.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| key_research_goal | string | Yes | Research question. Trimmed, 5–2000 characters. |
| business_context | string | No | Product context, 5–2000 characters when present. Shorter than 5 is 400. It is not copied from the goal. |
| additional_context_prompt | string | No | Extra interviewer guidance, max 1000 characters. |
| target_interviews | number | No | 1–200. Default 1. Reserves 10 credits per interview. |
| duration_minutes | number | No | 5–65. Default 12. |
| interview_mode | string | No | voice (default), text, or voice_and_text. |
| languages | string[] | No | One locale runs the interview in that language and leaves the picker off. Two or more let the participant choose. Empty, unknown, or duplicate values are 400. Omit it for English voice and questions spoken as written. Locales: en-US, en-GB, en-AU, en-IN, hi-IN, ta-IN, mr-IN, kn-IN, ur-IN, pt-BR, es-ES, es-419, fr-FR, de-DE, zh-CN, ja-JP, it-IT, th-TH, ms-MY, vi-VN, id-ID, ar-SA, ko-KR, sv-SE, ru-RU. |
| voice_gender | string | No | female (default on create) or male. The server picks the voice. Do not send a voice id. |
| enable_link_context | boolean | No | Default false. Query params on the interview link are ignored until this is true. |
| custom_link_variables | array | No | Up to 10 declared custom keys: { key, label?, default_value? }. key is snake_case, max 32, not reserved. |
| metadata | object | No | Your own key-value pairs. |
| study_media | object | No | { type: image | prototype, url, description? }. Shown with questions for web participants. |
Response
{
"study_id": "uuid",
"interview_link": "https://usercall.co/interview/shortid",
"target_interviews": 1,
"interview_mode": "voice",
"credits_reserved": 10,
"status": "running",
"study_media": null,
"link_context_enabled": false,
"custom_link_variable_keys": [],
"languages": [],
"voice_gender": "female",
"next_step": { "action": "simulate_interview", "reason": "..." },
"generated_guide": {
"ai_agent_intro_message": "...",
"key_learning_goals": "...",
"workflow_end_message": "...",
"workflow_questions": [
{ "id": "question_1", "text": "...", "order": 0, "follow_up_depth": "adaptive" }
]
}
}Example
curl -s -X POST "$BASE_URL/api/v1/agent/studies" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"key_research_goal": "Why do people drop out of onboarding after the first project?"
}'List studies
/api/v1/agent/studies
Lists studies on the personal account, each with the same interview link create returned.
When to call it
Call this to reuse a study for a research trigger, or to find a link again.
Response
{
"studies": [
{
"study_id": "uuid",
"title": "Why do people drop out of onboarding?",
"created_at": "2026-09-30T00:00:00.000Z",
"is_agent_api_study": true,
"archived": false,
"link_disabled": false,
"interview_link": "https://usercall.co/interview/shortid",
"link_context_enabled": false,
"trigger_eligible": true,
"interview_mode": "voice"
}
]
}Example
curl -s "$BASE_URL/api/v1/agent/studies" \
-H "Authorization: Bearer $USERCALL_API_KEY"Get study status
/api/v1/agent/studies/{study_id}
Returns progress, the interview link, and the next step.
When to call it
Call this before sharing, and poll it after interviews exist until status is complete.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | Path id from create. |
Response
{
"status": "running",
"completed_interviews": 0,
"target_interviews": 1,
"last_updated": "2026-09-30T00:00:00.000Z",
"interview_link": "https://usercall.co/interview/shortid",
"link_disabled": false,
"link_context_enabled": false,
"next_step": { "action": "simulate_interview", "reason": "..." }
}Example
curl -s "$BASE_URL/api/v1/agent/studies/$STUDY_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Update a study
/api/v1/agent/studies/{study_id}
Changes targets, the guide, interview mode, languages, or link context. The research question cannot be changed.
When to call it
Call this to apply a review suggestion, re-enable a link, or turn link context on. Send at least one field.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| target_interviews | number | No | 1–200. Increasing reserves 10 credits per added interview. Decreasing releases unused reserved credits. |
| is_link_disabled | boolean | No | Set false before sending the link, unless the study already hit its target. |
| interview_mode | string | No | voice, text, or voice_and_text. |
| ai_agent_intro_message | string | No | 1–5000 characters. |
| key_learning_goals | string | No | 1–5000 characters. Required before a guide review. |
| workflow_end_message | string | No | 1–5000 characters. |
| workflow_questions | array | No | Replaces the question list. Adaptive probing is isAdaptive true and followUpCount 3. followUpCount 3 without isAdaptive is rejected. |
| study_media | object | null | No | Replaces media on every question. null removes it. |
| languages | string[] | No | Same rules as create. One locale turns the picker off. Two or more turn it on. |
| voice_gender | string | No | female or male. Omit it to leave the stored voice gender. |
| enable_link_context | boolean | No | Omit it to leave the column unchanged. |
| custom_link_variables | array | No | Replaces the declared list. [] clears declarations. |
Response
{
"study_id": "uuid",
"target_interviews": 1,
"is_link_disabled": false,
"interview_mode": "voice",
"updated_at": "2026-09-30T00:00:00.000Z",
"languages": ["ko-KR"],
"voice_gender": "female",
"link_context_enabled": true,
"custom_link_variable_keys": ["company"]
}Example
curl -s -X PATCH "$BASE_URL/api/v1/agent/studies/$STUDY_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_questions": [
{ "text": "What were you trying to finish?", "isAdaptive": true, "followUpCount": 3 }
]
}'Delete a study
/api/v1/agent/studies/{study_id}
Deletes the study, its calls, and releases unused reserved credits.
When to call it
Call this when the study should not run again. This cannot be undone.
Response
{ "study_id": "uuid", "deleted": true, "credits_released": 10 }Example
curl -s -X DELETE "$BASE_URL/api/v1/agent/studies/$STUDY_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Delete call content
/api/v1/agent/studies/{study_id}/calls/{call_id}
Removes one call’s transcript, recording URLs, and summary.
When to call it
Call this to redact a single interview. Credits are not refunded.
Response
{ "study_id": "uuid", "call_id": "call_id", "deleted": true }Example
curl -s -X DELETE "$BASE_URL/api/v1/agent/studies/$STUDY_ID/calls/$CALL_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Link parameters
Query parameters on interview_link are stored only when enable_link_context is true.
When to call it
Turn the flag on at create or update before you put context on the participant URL. A trigger invite map turns the flag on for you.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| customer_profile | query | No | Fixed key. Max 200 characters after sanitizing. Longer values are dropped. |
| product_or_feature | query | No | Fixed key. Same length rule. |
| journey_moment | query | No | Fixed key. Same length rule. |
| recent_activity | query | No | Fixed key. Same length rule. |
| other_context | query | No | Fixed key. Same length rule. |
| external_id | query | No | The caller’s person id, not a Usercall id. |
| custom key | query | No | Must be declared. A declared default is stored when the query value is absent. Undeclared keys are dropped. {{key}} works in consent and email copy. |
Response
Values are untrusted participant context. By default the moderator uses them as background and does not say them back. They return on the interview.completed webhook as link_context, external_id, and custom_variables. They are not on get study results.Example
https://usercall.co/interview/shortid?customer_profile=pm&external_id=cus_1&company=AcmeStart a simulation
/api/v1/agent/studies/{study_id}/simulations
Starts a dry run and returns immediately with status running.
When to call it
Call this before sharing. Do not wait on the call for a transcript. Read it with the simulation id.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| persona.name | string | With persona | 1–80 characters. Both persona fields are required if you send persona. |
| persona.prompt | string | With persona | 1–4000 characters. Omit persona to use the default participant. |
Response
{
"study_id": "uuid",
"simulation_id": "uuid",
"status": "running",
"next_step": { "action": "simulate_interview", "reason": "..." }
}Example
curl -s -X POST "$BASE_URL/api/v1/agent/studies/$STUDY_ID/simulations" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Read a simulation
/api/v1/agent/studies/{study_id}/simulations/{simulation_id}
Returns the simulation status, and the transcript once the run is finished.
When to call it
Call this after start, using the simulation id. A finished pass, fail, or error lets status move on to share.
Response
While running, transcript is omitted:
{ "study_id": "uuid", "simulation_id": "uuid", "status": "running", "next_step": { "action": "simulate_interview", "reason": "..." } }
When finished:
{
"study_id": "uuid",
"simulation_id": "uuid",
"status": "pass",
"result_explanation": "...",
"transcript": [],
"simulated_speech_duration_seconds": 42,
"simulated_wall_clock_seconds": 90,
"next_step": { "action": "review_study", "reason": "..." }
}Example
curl -s "$BASE_URL/api/v1/agent/studies/$STUDY_ID/simulations/$SIMULATION_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Review the guide
/api/v1/agent/studies/{study_id}/reviews
Runs a guide review and returns findings and up to three suggested edits. It costs 1 credit.
When to call it
Call this before sharing, with an empty body. It does not read transcripts and does not apply edits.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| body | object | Empty | Send {} or omit the body. call_ids and any other field are 400 invalid_request. |
Response
{
"study_id": "uuid",
"review_id": "uuid",
"kind": "guide",
"credits_charged": 1,
"summary": "...",
"guide_findings": [],
"goal_assessments": [],
"recommendations": [
{ "kind": "guide", "title": "...", "rationale": "...", "label": "...", "suggested_change": {} }
],
"interview_moments": [],
"study_findings": [],
"next_step": { "action": "update_study", "reason": "..." }
}
Write suggested_change onto workflow_questions with update study. If recommendations is empty, next_step.action is share.Example
curl -s -X POST "$BASE_URL/api/v1/agent/studies/$STUDY_ID/reviews" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Get study results
/api/v1/agent/studies/{study_id}/results
Returns themes, quotes, and risks that explain why people behaved this way.
When to call it
Call this after status is complete. Empty themes while the study is running are not a finding.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| format | query | No | summary (default) or full. full adds transcripts and audio_urls. |
Response
{
"status": "complete",
"completed_interviews": 1,
"target_interviews": 1,
"themes": [
{ "name": "Setup friction", "summary": "...", "confidence": 0.4, "quotes": ["..."] }
],
"key_insights": ["..."],
"risks_or_unknowns": [],
"next_step": { "action": "done", "reason": "..." }
}
format=full also returns transcripts: [{ "call_id": "...", "transcript": "..." }] and audio_urls. In-progress responses still include empty transcripts and audio_urls. Link context is not on this payload.Example
curl -s "$BASE_URL/api/v1/agent/studies/$STUDY_ID/results?format=summary" \
-H "Authorization: Bearer $USERCALL_API_KEY"Get trigger capabilities
/api/v1/agent/triggers/capabilities
Says which trigger conditions exist and which do not.
When to call it
Call this before inventing a filter. Counts, sequences, absence, and time windows are not supported.
Example
curl -s "$BASE_URL/api/v1/agent/triggers/capabilities" \
-H "Authorization: Bearer $USERCALL_API_KEY"List trigger events
/api/v1/agent/triggers/events
Lists events the Usercall SDK has received in the last 30 days.
When to call it
Call this to confirm the product event exists before you create a trigger.
Response
{ "events": [], "setup_required": true, "hint": "No events have been received..." }
When events exist, setup_required is false and each event has event_name, sources, last_seen_at, sample_count, and intercept_capable.Example
curl -s "$BASE_URL/api/v1/agent/triggers/events" \
-H "Authorization: Bearer $USERCALL_API_KEY"Get an event schema
/api/v1/agent/triggers/events/{event_name}/schema
Splits an observed event into properties and traits, with types and sample values.
When to call it
Call this to pick exact filters. An unknown event is 404 event_not_observed.
Example
curl -s "$BASE_URL/api/v1/agent/triggers/events/study_tested/schema" \
-H "Authorization: Bearer $USERCALL_API_KEY"Get SDK setup
/api/v1/agent/triggers/setup
Returns the install snippet for the analytics provider and whether events are already arriving.
When to call it
Call this when list events is empty or a needed event has not been observed.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| provider | query | No | posthog (default), mixpanel, amplitude, segment, ga4, or custom. |
| events | query | No | Comma-separated event names to allow, max 50. |
Response
install_snippet, identify_snippet, allowlist_update_snippet, instructions, and status. The secret key is never returned.Example
curl -s "$BASE_URL/api/v1/agent/triggers/setup?provider=posthog&events=study_tested" \
-H "Authorization: Bearer $USERCALL_API_KEY"Create a research trigger
/api/v1/agent/triggers
Creates a paused trigger for people who did something in the product, and returns an activation URL.
When to call it
Call this when you know what happened and need to ask those people why. A human opens activation_url. Agents cannot activate it.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | Study that owns the interview. It needs an interview link. |
| event_name | string | Yes | One observed event, 1–120 characters. |
| properties | object | No | Exact-match event properties. Values are string, number, or boolean. |
| traits | object | No | Exact-match user traits. |
| url | object | No | { operator: equals | contains | starts_with, value }. |
| dwell_seconds | number | No | 1–600. Page-visit triggers only. |
| sampling_percent | number | No | 1–100. |
| cooldown_days | number | No | 0–365. |
| max_invites_per_day | number | No | 1–100. |
| delivery_method | string | No | intercept (default) or webhook. webhook requires a public https webhook_url. |
| invite_link_params | object | No | static, from_traits, and from_properties maps. A non-empty map turns enable_link_context on for the study. {} does not. |
Response
201 with the trigger, status "paused", activation_url, summary, warnings, and next_step telling you to show the link to a human.
Unknown condition keys are 422 invalid_trigger and nothing is written.Example
curl -s -X POST "$BASE_URL/api/v1/agent/triggers" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"study_id": "'"$STUDY_ID"'",
"event_name": "study_tested",
"traits": { "plan": "free" },
"sampling_percent": 25,
"invite_link_params": { "static": { "customer_profile": "pm" } }
}'List research triggers
/api/v1/agent/triggers
Lists triggers on the account with status and activation_url.
When to call it
Call this to see what is paused or active. activation_url is null once a human has activated the trigger.
Example
curl -s "$BASE_URL/api/v1/agent/triggers" \
-H "Authorization: Bearer $USERCALL_API_KEY"Get a research trigger
/api/v1/agent/triggers/{trigger_id}
Returns one trigger plus invite and interview counts.
When to call it
Call this to read status, activation_url, and stats for a trigger you already created.
Response
The trigger object plus stats: { "invited": 0, "interviews_started": 0, "interviews_completed": 0 }.
webhook_secret is never returned. webhook_secret_set says whether one is stored.Example
curl -s "$BASE_URL/api/v1/agent/triggers/$TRIGGER_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Update a research trigger
/api/v1/agent/triggers/{trigger_id}
Changes targeting, sampling, copy, or delivery. Changing an active trigger pauses it again.
When to call it
Call this to edit a trigger. Do not send status active. That is 409 and writes nothing.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| status | string | No | paused is allowed. active returns 409 activation_required and activation_url. |
| properties, traits, url, dwell_seconds, source | value or null | No | null clears that filter. |
Example
curl -s -X PATCH "$BASE_URL/api/v1/agent/triggers/$TRIGGER_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "sampling_percent": 10 }'Delete a research trigger
/api/v1/agent/triggers/{trigger_id}
Deletes the trigger.
When to call it
Call this when the trigger should stop matching. The study stays.
Response
{ "trigger_id": "uuid", "deleted": true }Example
curl -s -X DELETE "$BASE_URL/api/v1/agent/triggers/$TRIGGER_ID" \
-H "Authorization: Bearer $USERCALL_API_KEY"Verify interview.completed
A human saves an HTTPS URL under Home → Developer. Usercall POSTs interview.completed when a qualified interview has a summary and a transcript.
When to call it
Use this on your server to verify the body. The agent cannot set the URL. This is not the research-trigger webhook, which receives a matched user and an invite link.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| x-usercall-signature | header | Yes | t=<unix>,v1=<hex>. HMAC-SHA256 of `${t}.${rawBody}` with the signing secret. Compare v1 in constant time. No max clock skew is defined. |
| x-usercall-event-id | header | Yes | Same value as the idempotency-key header. Treat them as one delivery id. |
Response
{
"event": "interview.completed",
"event_id": "...",
"call_id": "...",
"interview_study_id": "...",
"interview_study_name": "...",
"interview_type": "voice",
"completed_at": "...",
"duration_seconds": 120,
"summary": "...",
"transcript_url": "https://app.usercall.co/api/webhooks/interview-completed/resource?token=..."
}
Optional: recording_url, screen_recording_url, link_context, custom_variables, external_id, email.
interview_type is text, video, or voice. Qualified agent voice interviews are included. Preview calls and failed or too-short calls are not. Retries run up to 3 attempts.Example
const crypto = require('crypto');
const [tPart, v1Part] = signatureHeader.split(',');
const timestamp = tPart.slice(2);
const v1 = v1Part.slice(3);
const expected = crypto.createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex');
const valid = expected.length === v1.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));Download the recording
/api/webhooks/interview-completed/resource?token=...
Downloads the signed recording or transcript from the webhook. No account auth.
When to call it
Call the recording_url within 24 hours. A valid token redirects 302 to storage. Text interviews omit recording_url. Screen recording is the separate screen_recording_url.
Response
recording_url and transcript_url are signed Usercall URLs.
Recording: 302 to storage, or 404 when there is no audio.
Transcript: 200 text/plain.
Missing token is 400. Invalid or expired token is 401.Example
curl -sL "$RECORDING_URL" -o interview.wavError envelope
Failed calls return JSON with error and message. Extra fields depend on the error.
When to call it
Branch on error, not on message text, except where a code below says otherwise.
Response
{ "error": "invalid_request", "message": "...", "details": {} }
400 invalid_json — body was not JSON
400 invalid_request — schema failed, a voice id was sent, or review included call_ids
400 review_not_ready — the study has no key learning goals
401 unauthorized
402 insufficient_credits — required, available, recommended_pack, checkout_url
404 study_not_found, simulation_not_found, call_not_found, trigger_not_found, event_not_observed
409 activation_required — activation_url, status. Nothing was changed
422 invalid_trigger — unknown_keys
429 study_limit_reached — limit 1 active agent study
429 simulation_limit_reached — limit 5 simulations per account per UTC day. Retell is not started
429 rate_limited
500 study_create_failed, guide_generation_failed, simulation_failed, review_failed, and the other *_failed codesExample
# A voice id is rejected. Send languages and voice_gender instead.
{ "error": "invalid_request", "message": "A voice id cannot be set. Send languages and voice_gender instead." }
# The sixth simulation in a UTC day.
{ "error": "simulation_limit_reached", "message": "You have reached the daily limit of 5 simulations. Please try again tomorrow.", "limit": 5 }Buy credits
/api/v1/billing/checkout
Creates a checkout URL when create or review returns 402.
When to call it
Open the checkout_url from the 402 body, or call this with the recommended pack. Creating a study does this for you.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pack_id | string | Yes | pack_50, pack_100, pack_300, or pack_500. |
| success_url | string | No | HTTPS URL after purchase. |
| cancel_url | string | No | HTTPS URL if the person cancels. |
Response
{ "checkout_url": "https://usercall.co/agent-checkout?checkout_token=..." }Example
curl -s -X POST "$BASE_URL/api/v1/billing/checkout" \
-H "Authorization: Bearer $USERCALL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "pack_id": "pack_50" }'Hosted connector
Connect an assistant when a person wants it to talk to customers and learn why. The tools below are the same operations as this API. A result is the API JSON plus http_status. Treat http_status of 400 or higher as a failure.
When to call it
Add https://mcp.usercall.co in Claude, ChatGPT, or Cursor and sign in with OAuth. No API key. There is no share tool and no tool that activates a trigger.
Example
https://mcp.usercall.cocreate_study
Start an interview from the question you need answered. You get a study and a participant link. You do not share that link yet.
When to call it
Call it when you have the question and no study for it. Do not call it to rewrite a question you already created.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| key_research_goal | string | Yes | The question, in plain language. 5–2000 characters. |
| business_context | string | No | What the product is, only if you have it. At least 5 characters. |
| languages | string[] | No | One locale runs the whole interview in that language. Two or more let the participant choose. |
| voice_gender | string | No | female or male. Do not send a voice id. |
Response
interview_link, study_id, and next_step.action simulate_interview. If credits are short, open checkout_url.Example
create_study({
"key_research_goal": "Why do people abandon onboarding after they create their first project?"
})simulate_interview
Hear the interview yourself before a real participant does.
When to call it
Call it after create_study and before you send the link. Leave simulation_id off to start. Call it again with that id to read the transcript. The start returns immediately. A simulation is not a completed interview, and the sixth one in a UTC day is refused.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | From create_study. |
| simulation_id | uuid | To read | Omit to start. Pass the id from the start when you want the transcript. |
| persona | object | No | name and prompt together, only when starting. Omit both to use the default participant. |
Response
Start: status running and simulation_id. Later read: pass, fail, or error, plus the transcript when the run is finished.Example
simulate_interview({
"study_id": "<study_id from create_study>"
})
simulate_interview({
"study_id": "<study_id from create_study>",
"simulation_id": "<simulation_id from the start>"
})review_study
Check the guide for leading questions and gaps before anyone is invited.
When to call it
Call it before you share. Pass only study_id. It costs 1 credit, does not read transcripts, and does not edit the guide. Write a suggested change with update_study.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | From create_study. Do not send call_ids. |
Response
summary, guide_findings, and up to three recommendations. If recommendations is empty, next_step is share. Otherwise next_step is update_study.Example
review_study({
"study_id": "<study_id from create_study>"
})update_study
Change the guide, the language, the number of interviews, or whether the link accepts context.
When to call it
Call it to apply a review suggestion, turn a disabled link back on, or turn link context on. Do not use it to change the research question. Delete the study and create another.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | The study to change. |
| workflow_questions | array | No | The new questions. Adaptive probing is isAdaptive true and followUpCount 3. |
| is_link_disabled | boolean | No | false before you send the link, unless the study already hit its target. |
| enable_link_context | boolean | No | true before query params on the link are stored. |
Example
update_study({
"study_id": "<study_id from create_study>",
"workflow_questions": [
{ "text": "What were you trying to finish when you left?", "isAdaptive": true, "followUpCount": 3 }
]
})get_study_status
Decide the next move for a study that already exists.
When to call it
Call it before you share, and again after interviews start until the study is complete. Do what next_step says. An early result is not a finding.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | From create_study or list_studies. |
Response
next_step.action:
simulate_interview — nobody real has been interviewed, and you have not finished a simulation or a guide review
share — send interview_link, or call create_research_trigger
poll_study_status — interviews are in progress
get_study_results — the study is complete
If link_disabled is true, call update_study with is_link_disabled false before you send the link, unless the study already hit its target.Example
get_study_status({
"study_id": "<study_id from create_study>"
})get_study_results
Read why people behaved this way: themes, quotes, and risks.
When to call it
Call it after get_study_status says the study is complete. Ask for format full only when you need the transcripts.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | The finished study. |
| format | string | No | summary (default) or full. |
Example
get_study_results({
"study_id": "<study_id from create_study>",
"format": "summary"
})list_studies
Find an interview you already created so you can share its link or attach a trigger.
When to call it
Call it when you do not have the study id. interview_link on each row is the same link create_study returned. trigger_eligible is false when there is no link.
Example
list_studies({})delete_study
Stop a study for good and release unused credits.
When to call it
Call it when that interview should never run again. This cannot be undone. The calls go with the study.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | The study to delete. |
Example
delete_study({
"study_id": "<study_id from create_study>"
})get_trigger_capabilities
See which ways you can choose who gets invited, and which ways you cannot.
When to call it
Call it before you design a trigger. You can match one event, exact property or trait values, a URL rule, and page dwell. You cannot ask for counts, sequences, "did not do X", or a time window.
Example
get_trigger_capabilities({})list_trigger_events
See which product events Usercall has actually received, so you only invite people for something real.
When to call it
Call it before create_research_trigger. If nothing is listed, call get_trigger_sdk_setup.
Example
list_trigger_events({})get_trigger_event_schema
See the real fields on one event so a filter uses a name and a value you have seen.
When to call it
Call it after list_trigger_events, before you set properties or traits. Properties and traits are different lists. Matching is exact.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| event_name | string | Yes | An event from list_trigger_events, such as study_tested. |
Example
get_trigger_event_schema({
"event_name": "study_tested"
})get_trigger_sdk_setup
Get the snippet that sends your product events into Usercall.
When to call it
Call it when the event you care about has never arrived. If you can edit the app, apply the snippet. Otherwise show it to the user. Then call list_trigger_events again. The snippet never includes a secret key.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| provider | string | No | posthog, mixpanel, amplitude, segment, ga4, or custom. |
| events | string[] | No | Event names to allow, such as study_tested. |
Example
get_trigger_sdk_setup({
"provider": "posthog",
"events": ["study_tested"]
})create_research_trigger
Ask the people who just did something in the product why they did it.
When to call it
Call it when you know the event and which study should interview them. The trigger is created paused. Show activation_url to a human. You cannot turn it on. A non-empty invite_link_params map turns link context on for that study.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| study_id | uuid | Yes | The study those people should take. |
| event_name | string | Yes | An observed event, such as study_tested. |
| traits | object | No | Exact user traits, such as { "plan": "free" }. |
| properties | object | No | Exact event properties. Do not put traits here. |
| invite_link_params | object | No | static, from_traits, or from_properties. Empty {} does not change the study. |
Response
status paused, summary, and activation_url. Hand that URL to a person.Example
create_research_trigger({
"study_id": "<study_id from create_study>",
"event_name": "study_tested",
"traits": { "plan": "free" },
"sampling_percent": 25,
"invite_link_params": { "static": { "customer_profile": "pm" } }
})list_research_triggers
See which invites are waiting for a person to approve, and which are already on.
When to call it
Call it to find activation_url for a trigger you did not just create. status and activation_url are on every row. activation_url is empty once a human has activated it.
Example
list_research_triggers({})get_research_trigger
Check one trigger: whether a person has approved it, and how many interviews it has produced.
When to call it
Call it when you have a trigger id. If it is still paused, hand activation_url to a human. You cannot activate it.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| trigger_id | uuid | Yes | From create_research_trigger or list_research_triggers. |
Example
get_research_trigger({
"trigger_id": "<trigger_id from create_research_trigger>"
})update_research_trigger
Change who gets invited, how often, or what they see.
When to call it
Call it to edit a trigger you already created. Do not send status active. That returns activation_url and changes nothing. Editing an active trigger pauses it until a human approves it again.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| trigger_id | uuid | Yes | The trigger to change. |
| sampling_percent | number | No | 1–100. |
| status | string | No | paused is allowed. active is refused. |
Example
update_research_trigger({
"trigger_id": "<trigger_id from create_research_trigger>",
"sampling_percent": 10
})delete_research_trigger
Stop inviting people for this trigger. Interviews that already finished stay on the study.
When to call it
Call it when that event should no longer ask anyone why. The study itself stays.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| trigger_id | uuid | Yes | The trigger to delete. |
Example
delete_research_trigger({
"trigger_id": "<trigger_id from create_research_trigger>"
})