Errors and limits
Error shape
Errors return a JSON body with a stable machine-readable code alongside the HTTP
status:
{
"error": {
"code": "not_found",
"message": "Workflow not found",
"request_id": "3f2a9c0e8b7d4e1f9a6b5c4d3e2f1a0b"
}
}Branch on the code, not on the message text - messages are written for humans and
follow the request's Accept-Language, so they change.
Responses also carry the same id in an X-Request-ID header. Include it when you
contact support about a failed request.
Two responses do not use this shape:
- Request validation. A body, path or query parameter that fails schema validation
returns
422with adetaillist, one entry per problem:{"detail": [{"type": "missing", "loc": ["body", "name"], "msg": "Field required", ...}]}. - Unknown routes. A path or method that does not exist returns
{"detail": "Not Found"}(or"Method Not Allowed").
Status codes
| Status | Meaning |
|---|---|
400 | The request was malformed or a value was invalid. |
401 | Missing or invalid API key (authentication_required). An API key sent as Authorization: Bearer is also a 401. |
402 | Out of credit (insufficient_credit) or over a plan limit (quota_exceeded). Retrying will not help until the balance or plan changes. |
403 | Authenticated, but not allowed to do this (permission_denied). |
404 | It does not exist, or it is not in this organization. |
409 | It conflicts with the current state - e.g. an agent already in the environment you promote it to. |
413 | An uploaded file is too large (file_too_large). |
422 | The request was understood but the content was not valid. |
429 | Too many requests (rate_limited), or too many live calls at once (concurrency_limit). |
5xx | Something went wrong on our side, or at an upstream model or telephony provider (502, 503). |
A resource in another organization returns 404, not 403. That is deliberate:
telling you something exists but is not yours is itself a disclosure.
Rate limits
Requests can be rate limited per organization, per API key and per client address. On
a rate_limited response, wait for the number of seconds in the Retry-After header
before trying again, and back off exponentially with jitter if it repeats - not a tight
loop.
Treat 429 as a signal rather than a number to hard-code: the ceilings are not a fixed
part of the API contract.
Concurrency
Separately from request rate, an organization can be capped on how many voice calls run at once. Over that ceiling a new call is refused rather than queued. An outbound phone call is refused only when it connects, not when you request it, so pace an outbound burst yourself. Campaigns already pace themselves against it.
Retrying
5xx and 429 are worth retrying. 4xx other than 429 will fail again the same
way, so fix the request instead.
There is no idempotency key: a request that reached the server before your client gave
up has run, and retrying it runs it again. That matters most for anything that places
a call. Before retrying a POST /calls that timed out, check the call list for the
first attempt.