API Reference

Base URL: https://usertold.ai

Use REST when you are building a custom backend, scheduled job, or product integration. Use the CLI for terminal automation and MCP when an agent should work through project-aware tools and resources.

The shipped OpenAPI 3.1 document is the source of truth for its registered endpoint paths, request fields, response schemas, and enums:

This page covers the stable integration contract and a few common recipes. It intentionally does not copy the generated endpoint catalog. The multipart upload bodies and provider webhook routes are not yet represented completely in OpenAPI, so their required inputs are documented below.

Authentication

Authenticated REST requests use a user-delegated bearer token:

Authorization: Bearer <token>

The CLI can complete browser login and expose the token for local automation:

usertold auth login
export USERTOLD_TOKEN="$(usertold auth token)"

Keep bearer tokens server-side. The embedded widget uses a public SDK key (X-Project-Key: ut_pub_... or the widget's data-project-key) only on SDK routes designed for participant traffic; it is not a replacement for a user token. Provider webhooks use the signature verification inputs documented below.

Project scope

Most builder operations are scoped by organization and project handles:

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

Project responses include org_handle, project_handle, and project_ref (org/project). Study and intake routes accept their ID or project-scoped handle where the OpenAPI parameter description says so.

Integration recipes

Resolve the current workspace

Read the authenticated profile first. Its personal_org_handle identifies the default workspace for project discovery and creation.

curl -sS https://usertold.ai/api/user/profile \
  -H "Authorization: Bearer $USERTOLD_TOKEN"

Create a project

curl -sS https://usertold.ai/api/orgs/acme/projects \
  -H "Authorization: Bearer $USERTOLD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Checkout research" }'

Use the returned project handle for later project-scoped calls. Create and activate the study and linked intake before embedding the widget; the exact write schemas live in OpenAPI.

Review evidence before creating work

curl -sS "https://usertold.ai/api/orgs/acme/projects/checkout/signals?type=struggling_moment&limit=10" \
  -H "Authorization: Bearer $USERTOLD_TOKEN"

Inspect the source quote, transcript or playback context, and product area before grouping evidence into a review packet. Creating a packet does not make it verified delivery work.

curl -sS https://usertold.ai/api/orgs/acme/projects/checkout/tasks/from-signals \
  -H "Authorization: Bearer $USERTOLD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Clarify failed checkout payment state", "signal_ids": ["sig_1", "sig_2"] }'

Push the work item to Linear or GitHub only after source review confirms it is ready.

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_id; when supplied, study_id must identify 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_id=checkout-study"

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

Upload a recording

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_id; when supplied, study_id must identify an active study. Supported audio formats are MP3, M4A, WAV, OGG, FLAC, AAC, and WebM. Supported video formats are MP4, WebM, and MPEG.

curl -sS https://usertold.ai/api/orgs/acme/projects/checkout/sessions/upload-video \
  -H "Authorization: Bearer $USERTOLD_TOKEN" \
  -F "audio=@interview.ogg" \
  -F "video=@screen.mp4" \
  -F "study_id=checkout-study"

A successful upload returns 201 with the created interview and queued: true.

Receive provider webhooks

These signature-verified routes receive native provider payloads for connected UserTold integrations. They are not generic outgoing-webhook configuration endpoints, and they are not currently listed 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 these through the corresponding GitHub, Linear, or billing integration. Preserve the raw request body for signature verification; do not parse and reserialize it first.

Errors and retries

Domain errors that expose retry guidance use 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 turn an ambiguous write timeout into repeated unbounded writes.
  • 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

Limits use independent 60-second buckets for each route. They are keyed by client IP unless the table says otherwise.

Integration routeLimit
Google sign-in start10 requests per IP
OAuth authorize20 requests per IP
OAuth token exchange10 requests per IP
OAuth dynamic client create5 requests per IP
OAuth dynamic client read, update, or delete20 requests per IP for each route
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
SDK widget install beacon120 requests per IP
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

A limited request returns 429 with an error field. The limiter does not currently send Retry-After, remaining, or reset headers, so use bounded client-side backoff.

Continue