Errors.
Every non-2xx response uses one envelope. The code is a stable machine-readable string — branch on it, not on the message text, which can change.
The envelope#
{
"error": {
"code": "invalid_request",
"message": "languages: must be a non-empty array of ISO codes (e.g. [\"ar\", \"en\"])"
}
}Validation messages name the offending field as field: problem, so one response tells you exactly what to fix. Fix the payload before retrying — replaying an invalid_request unchanged fails the same way.
Error codes#
| Field | Type | Description |
|---|---|---|
| invalid_request | 400 | The request failed validation: a missing or malformed field, a bad languages array, an unknown dialect or voice, a bad filter value or cursor. The message names the violation. |
| invalid_api_key | 401 | Missing Authorization header, malformed key, unknown key, or revoked key — deliberately indistinguishable. |
| insufficient_credits | 402 | The wallet can't cover a call that's about to start. Nothing was dialed or charged. |
| forbidden | 403 | Rare. The key is valid but lacks the scope for this action — e.g. a read-only key attempting a write. |
| not_found | 404 | The resource doesn't exist, or it belongs to another organization — the API doesn't distinguish. |
| agent_not_published | 409 | You tried to place a call or attach a number to an agent that has no published version. Publish it first. |
| number_unavailable | 409 | The phone number isn't active for that agent — it's pending, released, or attached elsewhere. |
| rate_limit_exceeded | 429 | Per-key request budget exhausted for the current 60-second window. Comes with a retry-after header in seconds. |
| server_error | 500 | Something broke on our side. Safe to retry with backoff; the failure is already logged and alerting. |
402 — insufficient credits#
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits: wallet can't cover the call. Top up or upgrade."
}
}The wallet is checked against the call before anything is dialed — a 402 always reflects your true balance, never a half-started call to clean up. Top up or turn on auto-recharge so a low balance can never drop a call mid-campaign. Balances and top-ups live on Usage.
409 conflicts#
A 409 means the resource exists but isn't in a state that allows the action. Two cases:
agent_not_published— the agent has no live version. Callpublishfirst; see Agents.number_unavailable— the number isn't active for that agent. Attach anactivenumber and confirm it's bound to the agent; see Phone numbers.
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": {
"code": "agent_not_published",
"message": "Agent ag_7Q2c51b84a07 has no published version. Publish it before placing calls."
}
}429 — rate limited#
The per-key request budget is exhausted. Wait retry-after seconds and retry — the window mechanics and client patterns are on Rate limits.