# Error Codes

> The error response format for the Edesy SIP-trunk and telephony API, and every error code with its HTTP status and fix.

Source: https://edesy.in/docs/sip-trunk-for-ai-voice-agents/api-reference/errors

Successful responses wrap the payload in `data`, with request details in `meta`. Errors return an `error` object and a non-2xx HTTP status:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "errors": [{ "field": "To", "message": "This field is required" }]
    }
  }
}
```

`details` appears only when there's more to say. For validation errors it holds `errors`, a list of `{ field, message }`. Branch on `code` and the HTTP status, not on `message`, which can change.

## General codes

| HTTP | `code` | Meaning | What to do |
|------|--------|---------|------------|
| 400 | `VALIDATION_ERROR` | A field is missing or invalid | Fix the fields listed in `details` |
| 401 | `UNAUTHORIZED` | API key missing, malformed or revoked | Send `Authorization: Bearer vp_…`; see [Authentication](https://edesy.in/docs/sip-trunk-for-ai-voice-agents/getting-started/authentication) |
| 402 | `INSUFFICIENT_BALANCE` | Wallet balance is too low | Top up in the portal |
| 403 | `FORBIDDEN` | The resource belongs to another account | Check the account SID or ID in the path |
| 403 | `NUMBER_NOT_OWNED` | The caller-ID isn't on your account | Use a number you own and have registered |
| 404 | `NOT_FOUND` | No such resource on your account | Check the ID |
| 409 | `NUMBER_NOT_OWNED` | (number purchase) Someone else just bought it | Pick another number |
| 409 | `QUOTE_STALE` | The price changed during purchase | Fetch the price again and retry |
| 409 | `CALL_ENDED` | The call has already ended | Nothing to hang up |
| 503 | `SERVICE_UNAVAILABLE` | No carrier capacity right now | Retry with exponential backoff |
| 500 | `INTERNAL_ERROR` | Unexpected server error | Retry; contact support if it persists |

## Number wiring codes

The wiring endpoints on [Phone Numbers](https://edesy.in/docs/sip-trunk-for-ai-voice-agents/api-reference/phone-numbers) (`/telephony`, `/carrier`, `/inbound`) use lowercase codes in the same envelope:

| HTTP | `code` | Meaning |
|------|--------|---------|
| 400 | `validation` | Invalid `:id` or body |
| 400 | `bind_failed` | The inbound binding was rejected; check the target fields |
| 404 | `not_found` | The number doesn't exist or isn't yours |
| 409 | `inbound_conflict` | Inbound is bound elsewhere; retry with `"force": true` |
| 409 | `inbound_bound` | Inbound is still bound; unbind first or pass `force=true` |
| 503 | `not_configured` | Provisioning is temporarily unavailable |
| 500 | `internal_error` | Unexpected server error; retry |

## Retrying safely

- Retry `500` and `503` with exponential backoff. Don't retry `4xx` without changing the request.
- `POST /phone-numbers/:id/carrier` is idempotent and safe to repeat.
- `POST /calls` is not idempotent. A retry after a timeout can place a second call, so check `GET /calls` (or your status webhooks) first.
