reference
Errors & status codes
Every /v1 endpoint returns errors in one envelope, so you write the
error-handling path once and it works for the whole API:
{
"error": {
"code": "insufficient_scope",
"message": "the API key is missing the required scope: stream:golive",
"requestId": "req_9f2c1a7b4e5d6f80",
"requiredScope": "stream:golive"
}
}
code— a stable, machine-readable string. Branch on this, not on the HTTP status or the human message. Codes are part of the compatibility promise (see Versioning); new codes may be added, existing ones will not change meaning within/v1.message— a human-readable explanation. For logs and developers, not for branching. The wording may change at any time.requestId— the same value returned in theX-Request-Idresponse header. Quote it when you ask us to look into a request.requiredScope— present only oninsufficient_scope. It names the exact scope your key is missing, so you can request a key that has it.
The X-Request-Id header
Every response — success or error — carries an X-Request-Id. If you send your
own X-Request-Id on the request, we echo it back; otherwise one is generated
for you. Treat it as an opaque string: its format is not part of the contract.
Log it on your side and you can correlate any call with our logs.
Codes and their HTTP status
| HTTP | code |
When it happens | What to do |
|---|---|---|---|
400 |
invalid_request |
Malformed input: a bad cursor, a non-numeric limit, a body that doesn't parse. |
Fix the request. Don't retry unchanged. |
401 |
invalid_key |
No credential sent; the API key is unknown, revoked, or expired; or the user access token is expired or invalid (access tokens last one hour). | For a key: check the Authorization: Bearer sk_live_… (or X-API-Key) header and mint a new key if needed. For a user access token: when reason is token_expired, refresh it and retry; otherwise, or if the retry also fails, sign the user in again. |
402 |
subscription_required |
The account behind the key has no active subscription for what the route does (creating a source, for example). | Not fixable in code; the account needs a plan. |
402 |
input_limit_reached |
The plan's limit on inputs is reached, so the source can't be created. | Remove an input or change plan. |
402 |
storage_quota_exceeded |
A media upload doesn't fit the account's storage allowance (the library is full, or the file is bigger than any single upload the account may hold). | Free space or change plan. |
402 |
payment_required |
A plan limit this table doesn't name yet. | As above; read message. |
403 |
insufficient_scope |
The key is valid but lacks the scope for this route. requiredScope names it. |
Issue a key that holds the named scope. |
403 |
forbidden |
Authenticated and in scope, but not allowed on this particular resource. | Not fixable by changing scopes. |
403 |
interactive_auth_required |
The credential is an API key, and the route is only for a signed-in user: managing API keys (/v1/account/api-keys) and DELETE /v1/account. |
Call it with a user access token, not a key. No scope fixes this. |
404 |
not_found |
The studio (or sub-resource) doesn't exist, or your account can't see it. | Verify the id. A resource you don't own reads as not-found. |
409 |
conflict |
The request is valid, but the resource's current state refuses it. | Change that state, then retry. |
409 |
chat_bridge_not_enabled |
A chat push to a destination whose custom chat bridge is off. | Turn it on: PATCH the destination with {"chatBridge": true}. |
409 |
chat_session_not_running |
A chat push while the studio has no chat session to deliver into. | Retry shortly; the session starts with the studio's chat. |
409 |
chat_bridge_not_attached |
A chat push in the seconds after the bridge was turned on, before it is attached. | Retry shortly. |
409 |
destination_has_platform_chat |
Turning the chat bridge on for a destination whose chat TRaX already reads from the platform. | Not fixable by retrying: the bridge would show the same chat twice. |
413 |
payload_too_large |
The body is well-formed but bigger than the route accepts. | Send less. |
429 |
rate_limited |
You exceeded a rate limit. | Back off for the Retry-After seconds, then retry. See Rate limits. |
503 |
write_disabled |
Write endpoints are switched off on this deployment. | Not retryable by you; there is no interval after which it starts working. |
503 |
contribute_disabled |
Contributing from a device is switched off on this deployment. | Same as write_disabled: a switch, not an outage. |
503 |
storage_disabled |
Object storage isn't configured on this deployment, so the storage routes have nothing behind them. | Same: a switch, not an outage. |
503 |
unavailable |
A dependency needed to serve the request is temporarily down (e.g. key verification). | Transient — retry with backoff. |
502 |
bad_gateway |
An upstream service returned an unexpected error. | Transient — retry with backoff. |
500 |
internal |
An unexpected error on our side. | Retry with backoff; if it persists, send us the requestId. |
How to handle errors
- Read
error.code, not the HTTP status alone. Different503s (write_disabled,contribute_disabled,storage_disabledvsunavailable) mean very different things — the first three are deliberate switches, the last is transient. - Retry only the transient codes (
unavailable,bad_gateway,internal, andrate_limitedafter itsRetry-After). Use exponential backoff. Never hot-loop a retry. - Never retry a
4xxunchanged —invalid_request,invalid_key,insufficient_scope,forbidden,interactive_auth_required,not_found,payload_too_largeand the402plan codes will fail identically until you change the request, the key, or the account. - Surface
requestIdin your own error reports. It's the fastest way for us to trace a failing call.
Auth errors specifically
- A 401
invalid_keymeans the credential itself is the problem (missing, unknown, revoked, expired). Re-check the header and the key. - A 403
insufficient_scopemeans the credential is fine but under-scoped.requiredScopetells you what to add. A key can only ever act within its owner's own account, so a 404 rather than a 403 can also mean "not yours".
See the full request/response shapes in the API reference and the credential model in Authentication.