# Errors, rate limits, and retries (/docs/api-reference/errors)

> Error response shapes, status codes, rate limits, and retry guidance for the OmniDimension REST API.

Every error from the API is JSON. No endpoint returns HTML, including
unknown paths and wrong-method requests, so clients and AI agents can
always parse the response.

## Error response shape [#error-response-shape]

Most endpoints return errors in this shape:

```json
{
  "error": "unauthorized",
  "error_description": "Missing or invalid API key"
}
```

Call and campaign endpoints that report business outcomes use a
`success` flag instead, with extra context where relevant:

```json
{
  "success": false,
  "plan_expire": false,
  "error": "Concurrency call limit exceeded"
}
```

`plan_expire: true` means the account balance or plan is the blocker;
top up or upgrade, then retry.

## Status codes [#status-codes]

| Code | Meaning                                                                                                   | What to do                                                                                     |
| ---- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| 400  | Invalid request: malformed JSON or missing required fields. `error_description` lists the missing fields. | Fix the request body. Do not retry unchanged.                                                  |
| 401  | Missing or invalid API key.                                                                               | Check the `Authorization: Bearer` header. Keys come from the dashboard.                        |
| 404  | No endpoint at this path. The response includes `docs` and `openapi` links.                               | Check the path against the [OpenAPI spec](https://docs.omnidim.io/openapi.yaml). Do not retry. |
| 405  | Path exists but not for this HTTP method. The `Allow` header lists valid methods.                         | Switch to a method from `Allow`.                                                               |
| 429  | Rate limited.                                                                                             | Back off and retry after a minute. See limits below.                                           |
| 5xx  | Server error.                                                                                             | Retry with exponential backoff.                                                                |

## Rate limits [#rate-limits]

* Provider and voice listing endpoints (`/api/v1/voices`, provider
  lists): **10 requests per minute per user/IP**. Exceeding it returns
  a JSON 429; recently cached data may be served while limited. These
  lists change rarely, so cache them client-side.
* Call dispatch is governed by your plan's **concurrency limit** (how
  many calls can run at once), not a request rate. Exceeding it returns
  `success: false` with a concurrency error; retry after active calls
  finish.
* Other CRUD endpoints have no hard published rate limit. Be a good
  citizen: batch where the API offers it (bulk campaigns instead of
  loops over dispatch) and poll the
  [live-status endpoint](/docs/api-reference/realtime) instead of
  refetching full campaign objects.

## Retry guidance for agents and SDKs [#retry-guidance-for-agents-and-sdks]

* **429 or 5xx:** retry with exponential backoff and jitter, starting
  at 1 to 2 seconds. The voice-list 429 window is one minute.
* **404:** do not blind-retry. Fetch the
  [OpenAPI spec](https://docs.omnidim.io/openapi.yaml) and re-resolve
  the endpoint name; LLM-remembered paths drift.
* **405:** read the `Allow` header and switch methods.
* **Dispatch retries:** there is no `Idempotency-Key` support yet, so a
  blind retry of `dispatchCall` can place a second call. Before
  retrying a timed-out dispatch, check
  [call logs](/docs/api-reference) for the original request's outcome.

## Service status [#service-status]

Live availability for the API, omnidim.io, SIP infrastructure, and the
MCP server: [status.omnidim.io](https://status.omnidim.io)

The status page is also machine-readable. Agents and monitors should
use the JSON endpoint instead of scraping the page:

```bash
curl https://status.omnidim.io/api/status-page/heartbeat/all
```

Returns recent heartbeats and an `uptimeList` with per-service uptime
ratios. Before retrying repeated 5xx errors, check it: if a service
shows degraded, back off rather than hammering the API.