# Error Codes

> Voice API error format, HTTP status codes and error codes such as VALIDATION_ERROR, INSUFFICIENT_BALANCE, NUMBER_NOT_OWNED and CONCURRENCY_LIMIT, with what to do about each.

Source: https://edesy.in/docs/voice-api/calls/errors

Voice API errors return a non-2xx HTTP status and a JSON body with an `error` object holding a machine-readable `code`, a human-readable `message` and, for validation errors, a `details` object. Branch on the HTTP status and `code`, not on the message text, which can change.

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Only one of 'text', 'url', 'voiceml', 'stream_url' or 'application_id' may be provided",
    "details": { "ignored_fields": ["Url"] }
  }
}
```

`details.ignored_fields` lists body fields the API did not recognise. On success the same list appears in `meta.ignored_fields`.

## Codes

| HTTP | Code | Meaning | What to do |
|---|---|---|---|
| 400 | `VALIDATION_ERROR` | A field is missing, out of range, or two instruction sources were sent | Read `message` and `details`; fix the request |
| 401 | `UNAUTHORIZED` | Missing or invalid credentials | Check the key or Account SID and Auth Token |
| 402 | `INSUFFICIENT_BALANCE` | Wallet too low for the call or purchase | Top up the wallet |
| 403 | `FORBIDDEN` | The Account SID in the path is not your workspace | Use your own Account SID |
| 403 | `NUMBER_NOT_OWNED` | `from` is not a number in your workspace | Use a number you own |
| 403 | `KYC_NOT_APPROVED` | Buying a number before KYC is approved | Complete KYC |
| 404 | `NOT_FOUND` | The call, number or resource does not exist in this workspace | Check the ID and workspace |
| 409 | `CALL_ENDED` | The call has already ended | Nothing to do |
| 409 | `QUOTE_STALE` | A number's price changed since you loaded it | Reload and retry |
| 429 | `CONCURRENCY_LIMIT` | All of the workspace's channels are in use | Retry after `Retry-After` seconds, or add channels in Billing |
| 429 | `CPS_LIMIT` | Calls started faster than the workspace's calls-per-second limit | Slow down; retry after `Retry-After` seconds |
| 500 | `INTERNAL_ERROR` | Something failed on our side | Retry with backoff; contact support with `meta.request_id` if it persists |
| 503 | `SERVICE_UNAVAILABLE` | No route is available right now, or all routes are at capacity | Retry shortly |

## Limit responses

`429` responses carry headers you can act on:

| Header | Meaning |
|---|---|
| `Retry-After` | Seconds to wait before retrying |
| `X-Channel-Limit`, `X-Channels-In-Use` | Channel limit and current usage (`CONCURRENCY_LIMIT`) |
| `X-CPS-Limit` | Calls-per-second limit (`CPS_LIMIT`) |

The body also includes `limit`, `in_use` and `retry_after`.

## Phone number routing errors

The number routing endpoints (`PUT /v1/phone-numbers/{id}/inbound` and related) use lower-case codes:

| HTTP | Code | Meaning |
|---|---|---|
| 400 | `validation` / `bind_failed` | Invalid ID or body, for example a target without its URL |
| 404 | `not_found` | Number not found in this workspace |
| 409 | `inbound_conflict` | The number is bound to something set up outside these APIs; pass `"force": true` to take it over |

## Call failures are not API errors

A call that is accepted (`201`) but then does not connect is not an API error. Its result arrives as `status`, `hangup_cause` and `call_outcome`. See [Call Events and Outcomes](https://edesy.in/docs/voice-api/calls/call-events-and-outcomes).
