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

# Errors

> Every API v2 error is {code, message, details} at the root. The codes you will meet, what they mean, and the details keys that tell you what to do next.

## One shape

```json theme={null}
{
  "code": "invalid_request",
  "message": "Invalid request body",
  "details": {
    "errors": [{ "path": ["to"], "message": "Required" }]
  }
}
```

There is no `{"error": {...}}` wrapper anywhere in v2. Branch on `code`; show
`message` to a person. A message never carries a stack trace or a source path.

## Details keys

Keys that mean the same thing on every error:

| Key | Meaning |
| - | - |
| `errors` | On every `400 invalid_request` for a bad body or query: `[{path, message}]`, with `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`). |
| `recommendation` | One sentence saying what to do next. |
| `dashboardUrl` | The dashboard screen that fixes it. |
| `connect` | On `409 no_channel`: what this project can connect to reach the recipient, each with the exact request, or `null` when it is connected from the dashboard. |
| `from` / `address` | The address the error is about, under the name the request used. |

## Codes

| Status | Code | Meaning |
| - | - | - |
| 400 | `invalid_request` | A field is missing or wrong. See `details.errors`. |
| 400 | `invalid_json` | The body is not JSON. |
| 400 | `duplicate_identifier` | Another contact already holds this identifier on this channel (`details.conflicts`). |
| 400 | `invalid_bot_token` | Telegram rejected the bot token (`details.telegramSaid`). Nothing was connected. |
| 401 | `unauthorized` | Missing or invalid API key. |
| 402 | `insufficient_balance` | The balance cannot cover it. |
| 402 | `paid_plan_required` | Needs a paid plan, for example buying a number. |
| 403 | `kyb_required` | One-way SMS is closed to this account until its business verification is approved. Not returned while one-way SMS needs no verification, which is the default. `details.dashboardUrl` is `/kyb`. |
| 403 | `ten_dlc_required` | SMS to a +1 number needs an approved 10DLC campaign. `details.connect` lists the registration calls, `details.alternatives` the channels that need none. |
| 403 | `channel_inactive` | The `channel` you named is connected but switched off. |
| 403 | `sending_suspended` | Sending from this account is suspended. Contact support. |
| 403 | `plan_limit_reached` | The plan has no room for another active connection. |
| 404 | `not_found` | The resource, or the address, is not in this project. |
| 405 | `method_not_allowed` | Known path, wrong method. `details.allowed` and the `Allow` header list the methods. |
| 409 | `no_channel` | Nothing this call could use can reach the recipient. `details.connect` says what to connect. |
| 409 | `ambiguous_from` | Two channel groups share the address. Pick one of `details.candidates`. |
| 409 | `conflict` | A repeated `idempotencyKey`: nothing was sent again, and the original is in `details.message` (or `details.campaign`). Also a template name this project already uses. |
| 409 | `invalid_status` | The campaign is in a state that does not allow this (`details.status`). |
| 409 | `email_domain_missing`, `email_domain_not_verified`, `domain_setup_failed` | The email address cannot be connected yet. `details.recommendation` has the fix. |
| 409 | `disconnect_unavailable` | This transport cannot be removed through the API. The message says what to do instead. |
| 409 | `channel_cannot_receive` | An agent cannot answer on one-way SMS. |
| 422 | `sms_compliance_blocked` | The content check refused an SMS, or could not run (the message says which). Nothing was sent or charged. `details.requiredFixes` and `details.suggestedCompliantVersion` say what to change. See [SMS](/guides/sending-messages/sms#content-check). |
| 429 | `rate_limited` | The per-project request limit. Wait for `Retry-After`. |
| 429 | `rate_limit_exceeded` | A narrower limit: one SMS per recipient per minute, or too many agent test runs for the project. The message says when to retry. |
| 429 | `daily_limit_exceeded` | A channel's daily sending ceiling (`details.limit`). Resets at 00:00 UTC. |
| 503 | `verification_unavailable` | The DNS check of an email domain could not run. Nothing changed; retry. |

Sending limits, plan allowances and reviews are the same as on v1. See
[Sending limits](/concepts/sending-limits).

## Retrying a 409

Every `409` is a settled answer: retrying the same request cannot change it.
It carries the header `x-should-retry: false`, on v1 and v2 alike, and the
SDKs honour it, so they raise the `409` as a `ConflictError` at once instead
of retrying it. A repeated `idempotencyKey` is the case to handle on purpose:
the original is in the error's body, and that is how a retry learns the first
attempt landed.

## Wrong method

A path under `/v2/messages`, `/v2/conversations`, `/v2/contacts`,
`/v2/channels`, `/v2/templates`, `/v2/campaigns`, `/v2/agents`, `/v2/webhook`
or `/v2/email-domains` called with a method it does not take answers `405`, not
`404`:

```json theme={null}
{ "code": "method_not_allowed", "message": "PUT is not supported here. This path takes GET, POST.", "details": { "allowed": ["GET", "POST"] } }
```

An unknown path is `404`.


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