> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verbeo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> How the Verbeo API reports errors — the standard envelope, HTTP status conventions, and the full reference of global and module-specific error codes.

Every error response from the Verbeo API uses the same shape, regardless of which resource you're calling. This page is the canonical reference — individual endpoint pages link here instead of redefining the format.

## Error envelope

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "campaign_name: campaign_name is required",
    "details": [
      { "field": "campaign_name", "message": "campaign_name is required" }
    ]
  }
}
```

<ResponseField name="success" type="boolean" required>
  Always `false` on error responses. Use this as your first branch — every Verbeo response is one of `{success: true, data}` or `{success: false, error}`.
</ResponseField>

<ResponseField name="error.code" type="string" required>
  A stable, machine-readable identifier in `SCREAMING_SNAKE_CASE`. Branch your error handling on this value, not on the human-readable message. Codes are part of the public contract — they will not be renamed without notice.
</ResponseField>

<ResponseField name="error.message" type="string" required>
  Human-readable explanation, suitable for surfacing to your end users or logs.
</ResponseField>

<ResponseField name="error.details" type="object[]">
  Present on `VALIDATION_FAILED`. One entry per failing field, each with a `field` path (dot notation, e.g. `contacts.0.phone_number`) and a `message`.
</ResponseField>

<Note>
  The envelope is the same on every resource. Three details on this page are specific to **Campaigns**: the `error.details` array on a validation error, `400 INVALID_JSON` for a body that is not JSON, and `405 METHOD_NOT_ALLOWED` for a wrong verb. **Assistants**, **tools**, **phone numbers** and **Create phone call** answer `code` and `message` only, report a body that is not JSON as `422 VALIDATION_FAILED`, and have no `405`: a wrong verb answers `404 NOT_FOUND`, like a path they do not serve. One exception: `GET`, `PUT`, `PATCH` or `DELETE` on a one-word path of assistants, or `GET`, `PUT` or `DELETE` on one of phone numbers, such as `/list`, and `GET` or `DELETE` on one of tools, is read as a request for an id and answers `422 VALIDATION_FAILED`.

  One answer from those resources is outside the envelope: a request with no `Authorization: Bearer vbr_…` header at all is a `401` whose body carries `errorMessage` instead of `error`. Check the HTTP status before you read `error.code`.
</Note>

## HTTP status conventions

| Status | Meaning |
| - | - |
| `400` | Malformed request (e.g. invalid JSON) |
| `401` | Missing, invalid, or expired API key |
| `402` | The workspace's balance does not cover the request |
| `403` | Authenticated but not allowed to access the resource |
| `404` | Resource does not exist or doesn't belong to your workspace |
| `405` | HTTP method not supported on this path |
| `409` | Conflict with current resource state |
| `422` | Request validation failed — see `details` |
| `429` | A limit was reached — retry later |
| `500` | Internal server error — usually safe to retry ([see below](#retrying-errors)) |
| `502` | Upstream service error |
| `503` | Temporarily unavailable — retry later |

## Global error codes

These codes can be returned by any endpoint.

| Code | Status | When it happens |
| - | - | - |
| `UNAUTHORIZED` | 401 | Missing/invalid `Authorization` header, or key is inactive/expired. |
| `FORBIDDEN` | 403 | Authenticated, but the key cannot access this resource. |
| `INVALID_JSON` | 400 | Request body is not valid JSON. |
| `VALIDATION_FAILED` | 422 | One or more fields failed schema validation. See `details`. |
| `NOT_FOUND` | 404 | Path didn't match any route, or the referenced resource is missing. |
| `METHOD_NOT_ALLOWED` | 405 | The path exists, but not for this HTTP verb. |
| `CONFLICT` | 409 | Resource state forbids the requested operation. |
| `INTERNAL_ERROR` | 500 | Unexpected server-side failure. Usually safe to retry with backoff ([see below](#retrying-errors)). |
| `ORCHESTRATOR_ERROR` | 502 | Internal upstream (campaign orchestrator) returned an error. |
| `PAYMENT_REQUIRED` | 402 | The workspace's balance does not cover the request. |
| `TOO_MANY_REQUESTS` | 429 | A limit was reached: concurrent calls, a number's dial rate, a daily cap. |
| `BAD_GATEWAY` | 502 | A carrier (the telephony provider behind a number) failed or refused. |
| `SERVICE_UNAVAILABLE` | 503 | Switched off on Verbeo's side for now. Nothing is wrong with your request. |

## Module-specific error codes

Each resource module defines its own codes for situations the global set doesn't cover. They follow the same envelope; the `code` value is the only thing that varies.

* [Campaigns error codes](/api-reference/campaigns/overview#error-codes)

<Note>
  A resource that defines codes of its own lists them on its overview page. The envelope shape above does not change.
</Note>

## Validation errors in detail

`VALIDATION_FAILED` (422) is the only error that includes a `details` array. Each entry pinpoints one offending field:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request failed validation (2 errors)",
    "details": [
      { "field": "campaign_name", "message": "campaign_name is required" },
      { "field": "contacts.0.phone_number", "message": "phone_number is required" }
    ]
  }
}
```

The `field` path uses dot notation, including numeric indices for arrays. Use this to map errors back to specific UI inputs in your client.

## Retrying errors

| Code | Retry? | Notes |
| - | - | - |
| `INTERNAL_ERROR` | Yes | Use exponential backoff. Likely transient — except a page of the assistants, tools or phone-numbers list past the last one, which fails the same way every time: stop at `next_cursor: null`. |
| `ORCHESTRATOR_ERROR` | Yes | Upstream blip. Backoff and retry. |
| `TOO_MANY_REQUESTS` | Yes | Wait for the limit to clear, then send the same request. A daily limit clears at the next UTC day: cap your retries. |
| `SERVICE_UNAVAILABLE` | Yes | Retry later. |
| `BAD_GATEWAY` | Conditionally | Safe for a read or a settings change. After [creating a call](/api-reference/calls/create#retrying), check that no call exists first. |
| `PAYMENT_REQUIRED` | No | Add funds, then retry. |
| `UNAUTHORIZED` | No | Fix the credentials, then retry. |
| `VALIDATION_FAILED` | No | Fix the request body, then retry. |
| `NOT_FOUND` | No | The resource doesn't exist for your workspace. |
| `CONFLICT` | Conditionally | Resolve the conflicting state (e.g. pause a running campaign before updating it), then retry. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.