API Reference

Base URL: https://usertold.ai

Use REST for custom backends and integrations, the CLI for terminal automation, and MCP for project-aware agents.

OpenAPI 3.1 owns registered paths, request/response schemas, and enums.

Multipart and webhook inputs not yet covered by OpenAPI appear below.

Versioning and deprecation

The current /api/... routes are stable major version 1. Backward-compatible fields and operations may be added within v1; breaking changes require a new major path beginning with /api/v2.

OpenAPI’s x-api-lifecycle records this implicit v1 policy; no version-selection header is supported. Before removal, UserTold publishes migration guidance and sends RFC 9745 Deprecation plus Link: rel="deprecation". A scheduled removal also sends RFC 8594 Sunset at least 90 days ahead. No REST route is currently deprecated or scheduled for removal.

Authentication

Authenticated REST requests use a user-delegated bearer token:

Authorization: Bearer <token>

Get a token for local automation with the CLI:

usertold auth login
export USERTOLD_TOKEN="$(usertold auth token --json | jq -r .token)"

Keep bearer tokens server-side. The widget's public SDK key works only on participant SDK routes; it cannot replace a user token. Provider webhooks use the signatures below.

Project scope

Builder operations use organization and project handles:

/api/orgs/:orgHandle/projects/:projectHandle/...

Responses include org_handle, project_handle, and project_ref (org/project); opaque storage IDs are not builder references.

Integration recipes

Resolve the current workspace

Read GET /api/user/profile; personal_org_handle identifies the default workspace.

Create a project

Send { "name": "Checkout research" } to POST /api/orgs/:orgHandle/projects.

Project creation provisions an active First user interview without Intake or placement restrictions. Install the Project snippet immediately. POST /api/orgs/:orgHandle/projects/:projectHandle/starter-study idempotently restores it only when no Studies exist. Study responses include nullable recruitment_url for direct links.

Review Evidence before creating a Finding

Query GET /api/orgs/:orgHandle/projects/:projectHandle/signals?type=struggling_moment&limit=10. Review quotes, page context and the interview transcript before grouping Evidence.

Send { "title": "Checkout payment failures", "evidence_refs": ["sig_1", "sig_2"] } to POST /api/orgs/:orgHandle/projects/:projectHandle/findings/from-evidence. Creation saves a draft and sends no external work.

After checking sources, synthesis, and project context, set research_state to reviewed. Send to Linear or GitHub explicitly; review alone creates no external work.

Import a transcript

Send multipart/form-data to /api/orgs/:orgHandle/projects/:projectHandle/sessions/import-transcript. The transcript file is required, must contain non-empty text, and must be no larger than 5 MiB. Optional fields are participant_name, participant_email, and study_ref; when supplied, study_ref must be the handle of an active study.

curl -sS https://usertold.ai/api/orgs/acme/projects/checkout/sessions/import-transcript \
  -H "Authorization: Bearer $USERTOLD_TOKEN" \
  -F "transcript=@interview.txt;type=text/plain" \
  -F "study_ref=checkout-study"

A successful import returns 201 with the created interview and queued: true. Processing continues asynchronously.

Upload a recording

The dashboard and CLI use resumable direct-to-R2 uploads up to 20 GiB. Processing derives audio, representative frames, OCR, and provenance before transcription. See /api/openapi for the resumable upload endpoint schemas.

For small CLI/MCP imports, send multipart/form-data to /api/orgs/:orgHandle/projects/:projectHandle/sessions/upload-video using one of these shapes:

  • media: one audio or video file, up to 25 MiB; or
  • audio: an audio file up to 25 MiB, plus optional video playback media up to 100 MiB.

Optional fields are participant_name, participant_email, and study_ref; when supplied, study_ref must be the handle of an active study. Supported audio formats are MP3, M4A, WAV, OGG, FLAC, AAC, and WebM. Supported video formats are MP4, WebM, and MPEG.

Returns 201 with the interview and queued: true.

Receive provider webhooks

These incoming provider webhooks require signatures and are not yet in OpenAPI.

Provider routeRequired signature inputReplay input
POST /api/webhooks/githubRaw request body and X-Hub-Signature-256 (sha256= HMAC); X-GitHub-Event selects the event typeX-GitHub-Delivery when supplied
POST /api/webhooks/linearRaw request body and Linear-Signature HMAC; payload must include a current webhookTimestampLinear-Delivery when supplied
POST /api/webhooks/polarRaw request body plus webhook-id, webhook-timestamp, and webhook-signatureSigned timestamp must be within five minutes

Configure webhooks in the provider integration; verify signatures against the raw body.

Errors and retries

Retry guidance uses this JSON envelope:

{
  "code": "OPTIONAL_CODE",
  "retryable": false,
  "error": "Description of what went wrong",
  "action": "Optional next step for the caller"
}
  • Treat retryable as the primary retry signal when it is present.
  • Retry 429 and retryable 5xx responses with bounded exponential backoff and jitter.
  • Do not automatically retry validation, authentication, permission, or payment errors; surface error and action to the operator.
  • Do not retry an ambiguous write timeout indefinitely.
  • Upload and background-processing operations may succeed asynchronously. Poll the documented status operation instead of resubmitting the original write.

Common statuses are 400 validation, 401 authentication required, 402 payment required, 403 forbidden, 404 not found, 429 rate limited, and 5xx service failure.

Rate limits

Buckets last 60 seconds, keyed per route and IP unless specified below.

Integration routeLimit
Google sign-in start10 requests per IP
OAuth authorize20 requests per IP
OAuth token exchange and refresh600 accepted per client for exchange and refresh separately; refresh also 30 per client/token
OAuth dynamic client create120 requests per IP
OAuth dynamic client read, update, or delete20 accepted requests per client, shared across all three routes
Agent registration30 requests per IP; claim-producing registrations also share a 5-request bucket
Agent claim trigger, claim completion, and revocation5, 10, and 60 requests per IP, respectively
SDK intake list, detail, and response30, 60, and 10 requests per IP, respectively
SDK interview create, runtime token, and consent20 requests per IP for each route
Legacy SDK audio form upload30 requests per interview
Conductor start10 requests per IP
Conductor transcription secret, STS secret, and realtime call5 requests per IP for each route
Project integration-key validation and health10 requests per IP for each route
Admin email delivery test6 requests per IP

General per-IP/session middleware returns IETF-draft RateLimit-Policy and RateLimit, legacy RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and X-RateLimit-*, plus Retry-After on 429. Atomic claim trigger/completion, email throttle, and OTP lockout expose only documented error/reason guidance; they do not publish remaining/reset state.

OAuth also limits anonymous token requests to 30 per IP and rejected requests to 240 per IP for each token exchange, refresh, or client-configuration lane. Throttles return slow_down.

SDK/OAuth OpenAPI responses describe quota headers. RateLimit-Reset is delay seconds; X-RateLimit-Reset is Unix seconds. Headers require the limiter to run.

External website recordings

Create guest invitations with POST /api/organizations/:orgHandle/recording-invitations using targetUrl and externalRef. Follow the returned launchUrl or text instructions. Finishing uploads to the organizer's fixed project. Poll the invitation for results; explicitly enable a viewing link to embed playback. Schemas, project-level creation, expiry and revocation are in OpenAPI.

Put this setup into practice

Copy the prompt and paste it into your AI assistant.

View prompt
Help me apply this setup guide to my UserTold project and verify that the integration works.

Read this guide first: https://usertold.ai/docs/api

Use my existing UserTold connection. If it is not connected, help me connect through https://mcp.usertold.ai/mcp and complete browser authorization. Ask for missing project details, use the available UserTold tools, and walk me through any steps that need the dashboard.