Error handling

Understand and handle API errors consistently.
View as Markdown

API errors use a consistent JSON format with one of the HTTP status codes documented below. An unknown path or unsupported method returns plain text instead, as described in the next section.

Error Response Format

An enveloped error response follows this structure:

1{
2 "success": false,
3 "error": "Human-readable error message",
4 "code": "invalid_request"
5}

Two responses are not JSON. If the path you requested does not exist, or exists but does not accept the method you used, you get a plain-text 404 Not Found or 405 Method Not Allowed instead of the envelope below — so do not assume every error body parses as JSON. A 404 about a resource, such as Session not found or Room not found, does use the envelope.

  • error — a human-readable, English-only message whose phrasing may change. Use it for display and diagnostics.
  • code — a stable, machine-readable class from a small closed set (see Error Codes). Branch on this. New codes are only added with a documented API change.

Successful responses omit the error and code body fields and use the response shape documented for the endpoint. Depending on the endpoint, a 2xx response includes "success": true or uses a bare result shape. Determine success from the HTTP status code, not the success field.

Error Codes

The code field is the stable contract for programmatic error handling. The closed set contains twelve classes, mapped to the HTTP status code(s) below.

CodeHTTP StatusMeaning
invalid_request400, 415, 422The request was malformed or semantically invalid — including a missing or malformed Authorization credential, bad JSON, a missing or out-of-range parameter, or a wrong/absent Content-Type.
payload_too_large413The request body exceeded the endpoint’s size limit.
result_set_too_large413A list endpoint’s full matched set exceeds the system ceiling. Distinct from payload_too_large (oversized request body): here the response would be too large. Narrow a sessions query with filters; limit is applied only after this check.
unauthorized401Credentials are missing or invalid.
forbidden403Authenticated, but not permitted to act on this resource (e.g. the caller ID is not on the allow-list, or a per-app cap was reached). Acting on another app’s resource is 404, not 403.
not_found404The addressed resource does not exist.
conflict409The request conflicts with the current state of the resource.
recording_not_active400A room-recording stop addressed a room with no active Recording: a failed precondition, not a concurrency conflict. No recording command or lifecycle event is produced.
room_full409A join was rejected because the room is at its member cap. A count cap, not a rate limit — there is no retry_after; free a slot by removing a member. Distinct from forbidden (the per-app room-count cap on room create).
rate_limited429A rate-limit bucket denied the request. The retry_after field gives the seconds to wait.
unavailable502, 503, 529The gateway is overloaded, a dependency is unavailable, or required support-managed configuration is absent. Retry only conditions documented as transient; configuration failures require support.
internal500An unexpected server-side failure.

The set is closed. These twelve codes are the complete list. Branch on code for programmatic handling and fall back to the HTTP status class for anything you do not special-case; do not pattern-match the error string.

W3C Trace Context

The API supports the W3C Trace Context traceparent and tracestate request headers. The gateway validates their W3C shape and ignores malformed values.

A valid incoming context is linked to a new server-owned trace with its own trace identifier.

Every response carries the server-owned traceparent. Log that response header with your request context and quote it when reporting a problem. The same trace is propagated through gateway commands and webhook delivery.

Transport. The Voice API is machine-to-machine (M2M), server-to-server HTTP/JSON: there is no CORS support and the gateway is not callable from a browser. Send Content-Type: application/json on requests with a JSON body. Request-body limits are documented under 413 Payload Too Large.

HTTP Status Codes

400 Bad Request

The request is malformed or contains invalid parameters. This includes a missing or malformed Authorization credential; a syntactically valid credential with an invalid API key returns 401 Unauthorized instead.

Error MessageCauseFix
Missing Authorization headerThe Authorization: Bearer header was not provided.Include Authorization: Bearer <app_uuid>:<api_key> in every API request.
Invalid app identifier formatThe app_uuid portion of the Bearer token is not a provisioned app_uuid.Use the identifier provided by support
Invalid request bodyThe JSON body could not be parsed.Ensure your request body is valid JSON with Content-Type: application/json.
Invalid commandThe command path in the URL is not recognized.Use a command path documented for the endpoint, such as answer, playback/play, or recording/stop.
urls is requiredA playback or play_and_get_digits command is missing the urls or prompt_files field.Include an array of HTTP/HTTPS URLs in the request body.
operation_uuid must be a valid UUIDThe operation_uuid field is not a bare lowercase RFC-4122 v4 UUID.Send operation_uuid as a bare lowercase v4 UUID (no prefix), or omit it and let the gateway mint one.

401 Unauthorized

Authentication credentials are missing or invalid.

Error MessageCauseFix
Missing API keyThe Bearer token does not include the API key after the colon.Include Authorization: Bearer <app_uuid>:<api_key> in every API request.
Invalid API keyThe provided API key does not match the current key for this app.Use the current key supplied by support.

403 Forbidden

You are authenticated, but the action is not permitted. A 403 indicates a policy or resource limit on a resource your application owns. A resource owned by another application returns 404 (see 404 Not Found).

Error MessageCauseFix
cid_not_allowedThe from on POST /v1/sessions:dial is not on this app’s caller-ID allow-list.Use a caller ID provisioned for the app, or ask support to update the allow-list.
Maximum number of rooms reached for this applicationThe per-app room cap was hit on POST /api/v1/rooms. This is a resource limit, not a rate limit. See Resource Limits.Delete unused rooms to free capacity, or contact support.
ForbiddenThe recording URL is invalid or expired.Use the URL from the latest recording lifecycle event before its expires_at.

404 Not Found

The requested resource does not exist. Sessions and rooms owned by another application also return 404.

Error MessageCauseFix
Session not foundNo active session exists with the given UUID, including when the session has already ended.Verify the session UUID and that the session is still active.
Room not foundNo room exists with the given room_id, including when the room has been destroyed.Verify the room_id and that the room still exists.

409 Conflict

The request conflicts with the current state of the resource.

Error MessageCauseFix
Session is still joining a conference roomThe session is joining another room and does not yet have a member ID. (A session already fully joined to a different room is not an error: POST /rooms/{room_id}/members auto-leaves the prior room and joins the target.)Wait for the room.member.joined event for the in-progress join, then retry.
Session is already muted / unmutedThe mute state is already what you requested.Check the current mute state before toggling. This is a state-conflict guard.
Request already in progressA request with the same Idempotency-Key is still being processed.Do not send another concurrent request. After the original request completes, retry the same request with the same key to retrieve its result. See the Idempotency guide.
Another room media transition is in progress / Room recording stop is already in progress / Room recording state changed; retry (code: conflict)A room-recording stop overlapped another room-media transition or a concurrent recording lifecycle update.Wait for the in-progress transition to finish, then retry against the room’s current state.
Room is full (code: room_full)The room is at its member cap (system default 250 unless the application has its own cap). The member was not seated. This is a count cap, not a rate limit.Remove a member to free a slot, or ask support to raise the member cap. There is no retry_after. See Resource Limits.

422 Unprocessable Entity

The request was well-formed but could not be processed. It is produced when an Idempotency-Key is reused with a different request body.

Error MessageCauseFix
Idempotency-Key reused with a different requestThe same key was sent with a different request body.Use a fresh key per distinct operation. See the Idempotency guide.

413 Payload Too Large

Two distinct conditions return 413, each with its own code. payload_too_large means the request body is too big; result_set_too_large means the response a list endpoint would return is too big. Branch on the code field to tell them apart.

For payload_too_large: the request body exceeds the per-route size limit. The default limit is 1 MiB; the per-session command route (POST /v1/sessions/{uuid}/{command}) has a 64 KiB limit.

For result_set_too_large: list endpoints (sessions, rooms, members) return all matching items by default. A full matched set above 50,000 items returns this error. A smaller limit does not avoid the check because it applies to the full matched set first. Narrow a sessions query with its filters. Room and member list endpoints have no narrowing filter.

Error MessageCauseFix
Request body too large (payload_too_large)The request body exceeds the route’s maximum size (1 MiB default; 64 KiB on session command routes).Reduce the payload size. Command payloads should contain only small fields (DTMF strings, file URLs, flags). Do not embed audio or other large data inline.
result_set_too_largeA list endpoint’s full matched set exceeds the system ceiling of 50,000 items. Sessions return Too many results to return; narrow your query using filters.; rooms and members return Too many results to return.For sessions, narrow the query with filters such as state or creation time. A smaller limit does not help. Rooms and members expose no narrowing filter or request-side workaround. This is unrelated to request-body size.

415 Unsupported Media Type

The request was sent without a Content-Type header, or the header value is not application/json. Routes that decode a JSON body require the header on every request that carries a body. Action endpoints that take no body (e.g. DELETE /rooms/{room_id}/members/{uuid}, /rooms/{room_id}/playback/stop) do not require it.

Error MessageCauseFix
Content-Type must be application/jsonThe request omits the Content-Type header or sets it to a value other than application/json (charset parameters such as application/json; charset=utf-8 are accepted).Set Content-Type: application/json on every request that carries a JSON body.

429 Too Many Requests

You have exceeded the rate limit. See Rate Limits for details.

Error MessageCauseFix
Command rate limit exceededA per-room/per-session per-command token bucket was exhausted.Slow down and retry after retry_after seconds. See Rate Limits.
Application rate limit exceededThe per-app command token bucket was exhausted.Slow down and retry after retry_after seconds. See Rate Limits.

500 Internal Server Error

An unexpected error occurred on the server.

Error MessageCauseFix
Internal error validating API keyThe server could not look up the app configuration because a required dependency is unavailable.Retry with backoff. If repeated attempts fail, contact support.
Internal errorAn unexpected condition occurred.Retry with backoff. If repeated attempts fail, contact support.

502 Bad Gateway

The gateway could not prepare an upstream media source for room playback. The response uses code: unavailable, so treat it as transient.

Error MessageCauseFix
Playback source unavailableThe gateway could not determine the size of a finite playback source or could not prepare the source for playback.Verify that the media URL is reachable and retry with backoff. If repeated attempts fail, contact support.

503 Service Unavailable

503 covers the capacity, dependency, and configuration conditions below. Follow the recovery action documented for the condition you receive; Recording service not available requires support-managed configuration and must not be retried.

Error MessageCauseFix
Recording service not availableRecording is not enabled on this gateway.Contact support to enable the recording service.
No source node availableNo capacity was available to host the room, so no room was created.This is a transient capacity condition. Retry with backoff.
Source node unavailableThe infrastructure hosting the room could not be reached, so the room playback or room recording command was not dispatched.Retry with backoff. If repeated attempts fail, contact support.
Service temporarily unavailableThe idempotency store is unavailable, so a request carrying an Idempotency-Key was not processed (fail-closed).Retry the same request with the same key. See the Idempotency guide.

529 Site Overloaded

The gateway has no outbound capacity. Retry this response as described below. 529 is a non-standard status code for overload.

Error MessageCauseFix
overloadedNo outbound capacity is available to place a new call.Wait for the duration in the Retry-After response header (also returned in the retry_after body field as seconds), then retry. Use exponential backoff after repeated responses.

Best Practices

  • Always check the HTTP status code first. A 2xx status means the request was accepted. Any other status indicates an error.
  • Branch on the code field, not the message. The code is a stable closed enum; the error string is human-readable and may change.
  • Capture the response traceparent. Log the server-owned response value alongside your request context and quote it when reporting a problem to support.
  • Propagate W3C trace context. Send a valid traceparent with the request and preserve tracestate when present.
  • Parse the error field for display only. Error messages are designed to be human-readable and actionable, but are not part of the programmatic contract.
  • Handle 429 with exponential backoff. When rate-limited, wait before retrying. Do not retry continuously without a delay.
  • Retry only documented transient 5xx conditions. Use backoff for overload and temporary dependency failures. Do not loop on support-managed configuration errors such as Recording service not available; contact support.