# Errors

The Vocenya API uses standard HTTP status codes and one JSON error shape, with a machine-readable code for every refusal you can act on.

Successful requests answer `200 OK`, `201 Created` (a record was created), `202 Accepted` (work was queued, like an outbound call or a test webhook) or `204 No Content` (a record was removed). Anything in the `4xx` range is a problem with the request; `5xx` means something went wrong on our side and is safe to retry later.

## Error shape

Every error is JSON with a human-readable `message` and a stable, machine-readable `code`. Branch on `code`, never on `message`, which may be reworded.

```json
{
  "message": "This API key does not have the leads:write scope.",
  "code": "missing_scope"
}
```

Validation errors (`422`, `validation_failed`) also list the problem with each field under `errors`:

```json
{
  "message": "The reason field is required.",
  "code": "validation_failed",
  "errors": {
    "reason": ["The reason field is required."]
  }
}
```

A refused outbound call also carries the calling `decision`, explained in [Outbound calls](https://vocenya.com/docs/outbound-calls).

## Status codes

| Status | Meaning |
| --- | --- |
| `401` | The API key is missing, invalid, expired or revoked (`unauthenticated`). |
| `403` | The key lacks a scope (`missing_scope`), or the account or record does not allow the action. |
| `404` | No record with that id belongs to your account, or the path does not exist (`not_found`). Records of other accounts are never visible. |
| `405` | The path does not accept this method (`method_not_allowed`). |
| `409` | The record is in a state that does not allow the action, like replying to a closed chat (`chat_closed`). |
| `422` | Validation failed, or the action was refused with a `code`. |
| `429` | Too many requests (`rate_limited`). Wait for the number of seconds in the `Retry-After` header. See [Rate limits](https://vocenya.com/docs/rate-limits). |
| `5xx` | An error on our side (`server_error`). Retry with backoff. |

## Error codes

| `code` | Status | Meaning |
| --- | --- | --- |
| `validation_failed` | 422 | A field is missing or invalid; see `errors`. |
| `unauthenticated` | 401 | The API key is missing, invalid, expired or revoked. |
| `missing_scope` | 403 | The key does not have the scope this request needs. |
| `not_found` | 404 | That record, or that path, was not found. Records of the other mode (live or test) count as not found. |
| `method_not_allowed` | 405 | The path exists but not with this HTTP method. |
| `rate_limited` | 429 | Too many requests for this key. Wait for `Retry-After` seconds. |
| `too_many_failed_attempts` | 429 | Too many requests with a missing or invalid key from your IP address. |
| `server_error` | 500 | Something went wrong on our side. Retry later. |
| `live_chat_unavailable` | 403 | Website live chat is not part of your plan. |
| `chat_closed` | 409 | The chat has ended; it cannot be replied to. |
| `opt_out_permanent` | 403 | A person who asked not to be called cannot be removed from the Do Not Call list. |
| `invalid_number` | 422 | The phone number is not a valid US number. |
| `speed_to_lead_unavailable` | 422 | A lead `callback` was requested but the account cannot place AI callbacks. |
| `too_many_endpoints` | 422 | The account already has 10 webhook endpoints. |
| `unsafe_url` | 422 | The webhook URL is not a public HTTPS address. |

Outbound call refusals have their own codes, listed in [Outbound calls](https://vocenya.com/docs/outbound-calls#refusals).

## Retrying safely

The API does not take an `Idempotency-Key` header, but the endpoints you are most likely to retry are safe to repeat:

- `POST /outbound-calls` for a number that already has a call from the API waiting returns that same call instead of queuing another.
- `POST /do-not-call` for a number already on your list answers `200` with the existing entry (a new entry is `201`).
- A lead `callback` reuses a callback still waiting for the same number.
- Webhook deliveries carry a unique event id (`Vocenya-Delivery`), so you can ignore a delivery you have already processed.

`POST /leads` without a callback always creates a new lead, so retry it only when you know the first request did not reach us (a connection error rather than a `5xx`).
