Skip to content
Documentation
Esc
navigateopen⌘Jpreview
On this page

Errors, limits & security

The error envelope, retry guidance, rate limits, and the security rules every integration must follow.

Every failure has a stable, machine-readable identity — and every credential has a strict handling rule. This page is the operational summary; the generated API reference is authoritative for per-endpoint response codes.

The error envelope

All errors use the same shape, with the HTTP status preserved:

{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "Session not found."
  },
  "timings": { "latency_ms": 123 }
}

Match on error.code, not on the message — codes are contract, messages are prose.

Core error catalog

The codes you will actually meet in the canonical flow:

Code Status Meaning What to do
missing_api_key 401 No Authorization header Send Authorization: Bearer <api_key>
invalid_api_key 403 Key unrecognized or revoked Check the key; create a new one if revoked
invalid_body 400 Body is not valid JSON Fix serialization
invalid_session_id 400 Session id malformed Ids start with session:
not_found 404 No route matched, or session absent/not yours Verify id and key ownership
session_conflict 409 Session state conflicts with the operation See the lifecycle guide
rate_limited 429 Too many requests in the window Wait Retry-After seconds, then retry
internal_error 500 Unexpected server error Retry with backoff; contact support if persistent
modal_start_failed 502 Rendering backend rejected the start Retry with a new session
modal_unreachable 500 Rendering backend unreachable Retry after a brief wait
api_auth_not_configured 503 Server has no keys configured Platform-team action required

The full per-endpoint response sets — including the 4xx/5xx entries for each of the six operations — are rendered on their generated pages in the API reference.

Error taxonomy on the media WebSocket

The media WebSocket has its own error surface ({ "type": "error", "code": "...", "fatal": false } messages on the socket):

  • Fatal (socket closes): only invalid_session — recover by creating a new session.
  • Lifecycle (socket stays open): e.g. avatar_not_found, session_already_active.
  • Pipeline, per-turn (session survives): stt_failed, llm_failed, tts_failed, avatar_render_failed — the avatar may miss the turn, but the next one can succeed.

Details in the WebSocket guide.

Retries

  • Retry with backoff: 429 (after Retry-After), 500, 502, 503 transport-class errors.
  • Do not blind-retry: 409 conflicts mean the session state moved on — re-read the session status instead.
  • Idempotent-by-nature: status reads are always safe to retry.
  • End is deliberately not idempotent: a 409 on re-end means the session is already terminal — treat it as success in cleanup logic.

Rate limits

All /v1/ routes carry per-tenant abuse-control limits. They are safety nets, not billing quotas — nothing is counted or billed.

Category Limit (per 60 s window)
Session create (POST /v1/sessions) 20
Read (GET routes) 120
Cleanup (end calls) Exempt

Limits are enforced per tenant and per edge location, with a 60-second window. When exceeded, requests return 429 rate_limited with a Retry-After header carrying the remaining window:

{
  "success": false,
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded for category \"session_create\". Retry after 60 seconds."
  },
  "timings": { "latency_ms": 2 }
}

Correlation ids for support

Every /v1/ response carries an X-Request-Id header (also in the timings object of JSON responses). Supply your own on request to correlate across your systems, and include it when contacting support — it links your request to the platform’s server-side traces end to end.

Security rules

Credential boundaries

Credential Lives where Never
API key Backend secret / env binding In frontend code, URLs, source, or logs
Provider tokens (client_secret, access_token, …) Your backend, minted per session, passed in provider_config Sent to the browser, persisted, or logged
WebSocket token (?token= in ws_url) The browser, for the connection only Logged, screenshotted, or stored in analytics

Provider credentials are memory-only

Provider credentials passed in POST /v1/sessions are held in memory for the session lifetime only, then zeroed. They are never written to any database, cache, file, or log. Your backend must mint a fresh provider token for every session — treat provider tokens as short-lived by design.

Treat ws_url as a credential

The ?token= query parameter authorizes the media WebSocket on its own:

  • Do open the connection with the URL as-is (no extra headers).
  • Do use client-token refresh for reconnects.
  • Do not log it, put it in error reports, or persist it in browser storage.

Key hygiene

  • Create keys via POST /v1/api-keys; the plaintext is shown exactly once.
  • Store only backend-side; rotate by revoking (DELETE /v1/api-keys/{key_id}) and re-creating.
  • A revoked key stops working immediately.

Reporting a security concern

Found a potential vulnerability? Do not open a public issue. Contact the platform team directly through your account channel.

Was this page helpful?