Developer platform · v2

Reference

View as MarkdownAll docs

Errors

The error body#

Every failure is FastAPI's own envelope — no second wrapper, no vendor code:

json
{ "detail": "API key is missing the required scope 'voice:write'" }

detail is a sentence for the developer. On a 422 it is instead an array of { loc, msg, type } validation items, where loc names the offending field. One retired route returns a structured { code, message, migrate_to } object, which is why the declared type is string | object rather than string.

Every status this API returns#

The cause column is the backend's own declaration — from the OpenAPI responses and devplatform/schemas/api_errors.py — not a generic HTTP table. A status that is not listed here is not one this API raises deliberately.

StatusWhat causes itWhat to do
400A hard limit was reached: too many projects, or too many active keys on this project. A requested scope is not a scope this platform defines.Fix the request or free a slot — the message names the limit that was hit.
401No account session was presented, or it is invalid or expired. Missing API key or Invalid API key — the X-Api-Key header is absent, unknown, or revoked.Re-authenticate. For a console session refresh once, then send the user to sign in; for a project key, check the header and that the key is not revoked.
402Wallet balance cannot cover this session's maximum duration. Wallet balance cannot cover this call's maximum duration.Top up the wallet, or lower max_duration_seconds so the hold fits the spendable balance.
403The account is suspended or its email is not verified. API key is not scoped to a project, Project unavailable, or API key is missing the required scope '<scope>'.Verify the account's email, or mint a key with the scope the message names. Never retry unchanged.
404The project or the addressed resource does not exist for this owner. A resource owned by another developer is deliberately indistinguishable from one that never existed.Treat as absent, never as a permissions hint — another owner's resource and one that never existed are deliberately the same answer.
409The project or resource is in a state that refuses this change.Read the state and resolve it — verify the carrier, re-enable the endpoint, rename the duplicate — then retry.
422The request body or query is not valid. detail is an array of {loc, msg, type} and loc names the offending field.The body failed schema validation. detail is an array of {loc, msg, type}; the loc path names the offending field.
429The project's api_rpm ceiling was exceeded. Honour Retry-After; X-RateLimit-* headers are on every response.Back off for Retry-After seconds. X-RateLimit-Limit, -Remaining and -Reset are on every response, so pace before you are refused.
502The payment gateway did not create an order.An upstream (payment gateway, carrier) did not answer usefully. Safe to retry with backoff.
503The order could not be recorded locally, so no checkout was returned. BYOK provider accounts are unverified/unavailable, or the service is draining. The carrier connection or BYOK pipeline is unavailable. Webhook secret storage (the credential keyring) is unavailable.A dependency is unavailable or the service is draining. Retry with backoff; do not treat it as a permanent failure.

Rate limits#

Machine routes are counted per project against that project's api_rpm, in a fixed one-minute window. Every response — not only a refusal — carries:

HeaderMeaning
X-RateLimit-Limitthe project's requests per minute
X-RateLimit-Remainingwhat is left in this window
X-RateLimit-Resetseconds until the window rolls
Retry-Afteron a 429 only; wait this long

Pace against X-RateLimit-Remaining rather than waiting to be refused. The limiter fails open: if its store is unreachable your requests are allowed through, because a limiter protects other tenants and is not a security control.

WebSocket close codes#

The media socket refuses with an application close code rather than a status:

CodeMeaning
4401bad or expired stream token, or a token/session mismatch
4404unknown voice session
4408the session configuration expired before you connected
4409the session is not connectable, or the project changed after admission
4429the project's concurrency limit is full
4453the provider pipeline is unavailable
1013the service is restarting; reconnect with a new session

Retrying safely#

  • 429, 502, 503 — retry with exponential backoff and jitter. Honour Retry-After when it is present.
  • 402 — do not retry until the wallet is funded; the answer will not change.
  • 400, 403, 404, 409, 422 — never retry unchanged. Each one names something you must alter in the request or in the resource's state.
  • Outbound calls take an Idempotency-Key. Reuse the same key when you retry so a network timeout cannot dial twice; the repeat returns the first call with duplicate: true.

Truthful states#

The API reports what it actually knows, and the console shows those words verbatim rather than smoothing them over:

  • A saved credential is configured until the provider confirms it; only a verified account takes calls. A provider outage during a probe leaves it configured, never optimistically verified.
  • A carrier connection the carrier rejected is invalid, carrying the carrier's own message.
  • A voice session is billed for connected seconds only, rounded up to whole minutes, and settled exactly once.
  • A webhook test returns 202 when the delivery is recorded, not when it is delivered. Read dead-letters for deliveries that never landed.

Use one project per boundary

Provider secrets stay encrypted and project API keys stay on your server.