Skip to content

Errors

Every error uses a standard HTTP status code and a small JSON body. There is one shape for the whole API. You never need a different parser for a different endpoint.

A failed request returns the matching status code and:

{ "detail": "Call not found." }

detail is a human-readable string meant for your logs, not for parsing. Switch on the status code (and, for call outcomes, the reason code), not on the text.

Status Meaning What to do
400 Bad request. A field is missing, malformed, or not one of the allowed values. Check detail; it names the field.
401 Missing, unknown, revoked, or suspended-account API key. Check the Authorization: Bearer header. See Authentication.
402 Insufficient wallet balance to complete the action (acquiring a number, placing a call). Top up the wallet, then retry. See Wallet.
403 The account is suspended, or the action is blocked for this resource (e.g. a destination outside your allowlist). Contact support if this is unexpected.
404 The resource does not exist, or does not belong to your account. Double-check the uuid/sid. We never leak another account’s resources as a 403.
409 Conflict. The resource is in the wrong state (a number already taken), or a duplicate Idempotency-Key is in flight. See idempotency.
429 Too many requests. A per-account traffic cap. Back off and retry; for calls the refusal also carries a LIMIT.* reason code naming which cap.
500 Something broke on our end. Safe to retry (idempotently for money-moving calls). If it persists, contact support with a timestamp.

An originate that returns 402 or 429 never creates a call. Nothing was dialled, nothing was billed.

That is different from a call that connects and then fails partway through: that failure shows up as a normal 200 response whose Call object carries a terminal reason_code instead.

Both paths are covered by the same reason code table, so you can build one handler for “why didn’t this call work” regardless of which stage it failed at.