Skip to content
Documentation
Esc
↑↓navigate↵open⌘Jpreview
On this page

Errors, limits & security

The flat error shape, the AVTR_* error catalog, rate-limit headers, 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 shape

Every 4xx/5xx response uses the flat AVTR error shape — no envelope, no nested error object:

{
  "code": "AVTR_SESSION_NOT_FOUND",
  "message": "Session not found.",
  "request_id": "3f2a9c1e-...",
  "doc_url": "https://<orvyn-public-api-host>/docs/errors"
}
Field Present Meaning
code always Stable AVTR_* identifier — match on this, never on message
message always Human-readable, provider-agnostic prose
request_id always Correlation UUID; echoes the X-Request-Id response header
doc_url always Absolute HTTPS URL documenting this code
retry_after retryable codes only Seconds to wait (1–3600) before retrying

retry_after is required on retryable codes and forbidden on non-retryable codes — its presence tells you which kind you got. Honor the value and apply jitter when retrying.

Error catalog

All codes carry the AVTR_ prefix. The taxonomy has two classes:

Retryable codes (retry_after required)

Code Typical situation What to do
AVTR_PROVIDER_DOWNSTREAM_FAILED Upstream provider returned an error or a downstream step failed Retry after retry_after
AVTR_PROVIDER_TIMEOUT Provider call timed out Retry after retry_after
AVTR_PROVIDER_UNAVAILABLE Provider unavailable, session state unavailable, or capacity exhausted Retry after retry_after
AVTR_RATE_LIMIT_CONCURRENT_SESSIONS Concurrent-session limit hit Wait, then retry
AVTR_RATE_LIMIT_EXCEEDED Too many requests in the window Honor Retry-After, then retry
AVTR_RATE_LIMIT_PROVIDER_QUOTA Provider quota exhausted Wait, then retry
AVTR_RENDER_CAPACITY Rendering capacity exhausted Retry after retry_after
AVTR_RENDER_TIMEOUT Provider or render timeout Retry after retry_after
AVTR_SESSION_STATE_UNAVAILABLE Session state temporarily unavailable Retry after retry_after
AVTR_SFU_CONNECTION_FAILED SFU connection failed Retry after retry_after
AVTR_SFU_NEGOTIATION_FAILED SFU negotiation failed Retry after retry_after
AVTR_SFU_ROOM_FULL SFU room at capacity Retry after retry_after

Non-retryable codes (retry_after forbidden)

Code Typical situation What to do
AVTR_AUTH_EXPIRED_TOKEN Bearer credential expired Obtain a fresh credential
AVTR_AUTH_FORBIDDEN Credential recognized but not permitted — API keys: key lacks the caller’s required permission; Clerk: valid token rejected by the authorized-parties (azp) allowlist Use a key with the required permission / present the token to an allowed origin
AVTR_AUTH_CLERK_ORG_UNMAPPED Valid Clerk credential but the Clerk org is not mapped to an AVTR org Map the org or contact support
AVTR_AUTH_INVALID_KEY Key unrecognized or revoked Check the key; create a new one if revoked
AVTR_AUTH_MISSING_CREDENTIALS No key provided Send Authorization: Bearer <api_key> or x-api-key
AVTR_AUTH_MISSING_SCOPE Key lacks the route’s scope Re-create the key with the needed scope
AVTR_PROVIDER_MISCONFIGURED provider_type / provider_config mismatch or unknown provider Fix the request body
AVTR_RENDER_FAILED Rendering failed terminally Create a new session
AVTR_RENDER_MISCONFIGURED Render configuration invalid Fix the session configuration
AVTR_SESSION_CANCELLED Session was cancelled Create a new session
AVTR_SESSION_CONFLICT /end posted against created / ending / failed Treat as cleanup-success or re-read status
AVTR_SESSION_ENDED /end re-posted against ended / ended_with_warnings Treat as success in cleanup logic
AVTR_SESSION_NOT_FOUND Session absent — or belongs to another tenant (identical response either way) Verify the id and key ownership
AVTR_SFU_NOT_CONFIGURED SFU requested but not available for the account Use another transport

The per-endpoint status codes are documented on each generated reference page; the catalog above tells you which class a code belongs to and how to react.

Error taxonomy on the media WebSocket

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

  • 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: retryable codes — the response tells you so by carrying retry_after (and 429 responses carry the Retry-After header). Honor the value, add jitter.
  • 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

Every /v1/ response carries rate-limit headers:

Header Meaning
X-RateLimit-Limit Maximum requests allowed in the current window
X-RateLimit-Remaining Requests remaining in the current window
X-RateLimit-Reset Unix epoch seconds at which the window resets

When a request exceeds the limit it returns 429 with a Retry-After header — seconds until the rate-limit window resets — and a flat error body whose code is one of the rate-limit codes above (AVTR_RATE_LIMIT_EXCEEDED, AVTR_RATE_LIMIT_CONCURRENT_SESSIONS, or AVTR_RATE_LIMIT_PROVIDER_QUOTA), with the matching retry_after value in the body:

{
  "code": "AVTR_RATE_LIMIT_EXCEEDED",
  "message": "Rate limit exceeded. Retry later.",
  "request_id": "3f2a9c1e-...",
  "doc_url": "https://<orvyn-public-api-host>/docs/errors",
  "retry_after": 30
}

Correlation ids for support

Every /v1/ response carries an X-Request-Id header, and error responses echo the same value as request_id in the error body (ErrorResponse.request_id). Success bodies are bare schema objects and carry no such field. Supply your own on request (1–128 characters of [a-zA-Z0-9_-]) 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
Client token (?token= on the media WebSocket) 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 the client token as a credential

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

  • Do build the WebSocket URL with the token and open the connection as-is (no extra headers).
  • Do use client-token refresh for reconnects.
  • Do not log the token or the assembled URL, 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?