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

# Errors

> Error codes, formats, and how to handle them

The Tanvik API uses conventional HTTP status codes and a consistent JSON error body so failures are easy to handle programmatically.

## Error response shape

```json theme={null}
{
  "error": {
    "code": "invalid_request",
    "message": "to is required",
    "status": 400
  }
}
```

## HTTP status codes

| Status | Meaning                                                          |
| ------ | ---------------------------------------------------------------- |
| `200`  | Request succeeded                                                |
| `400`  | Malformed request — check required fields and formats            |
| `401`  | Missing, invalid, or revoked API key                             |
| `403`  | Key is valid but the account's plan doesn't include this feature |
| `404`  | Resource (message, template, webhook) not found                  |
| `422`  | Request understood but rejected downstream (WhatsApp, OTP logic) |
| `429`  | Rate limit exceeded — 120 requests/minute per key                |
| `500`  | Something went wrong on our end                                  |

## Error codes

| Code                      | Status | Cause                                                                                                                   |
| ------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `missing_api_key`         | 401    | No `Authorization` header sent                                                                                          |
| `invalid_api_key`         | 401    | Key is malformed, doesn't match any account, or was revoked                                                             |
| `feature_not_enabled`     | 403    | Your plan doesn't include API access                                                                                    |
| `rate_limited`            | 429    | More than 120 requests/minute on this key — see `Retry-After` header                                                    |
| `invalid_request`         | 400    | A required field is missing or malformed                                                                                |
| `no_default_otp_template` | 422    | No approved authentication-category template exists; create one or pass `template` explicitly                           |
| `otp_send_failed`         | varies | WhatsApp/OTP send failed downstream — `message` has the underlying reason                                               |
| `otp_expired`             | 422    | The OTP code has passed its validity window                                                                             |
| `otp_max_attempts`        | 422    | Too many incorrect verification attempts for this `otp_id`                                                              |
| `otp_not_found`           | 404    | No pending OTP matches the given `otp_id`                                                                               |
| `otp_verify_failed`       | varies | Unexpected failure verifying the code                                                                                   |
| `template_create_failed`  | varies | Template submission was rejected — `message` includes the reason                                                        |
| `template_not_found`      | 404    | No template with that `id` on this account                                                                              |
| `send_failed`             | varies | Message send failed — check `message` for the specific reason (e.g. closed session window, insufficient wallet balance) |
| `too_many_recipients`     | 400    | More than 100 recipients in one `/v1/messages` call                                                                     |
| `message_not_found`       | 404    | No message with that `id` on this account                                                                               |
| `webhook_not_found`       | 404    | No webhook with that `id` on this account                                                                               |
| `not_found`               | 404    | No matching route for this method/path                                                                                  |
| `internal_error`          | 500    | Unexpected server-side failure                                                                                          |

## Handling `429`s

Rate-limited responses include a `Retry-After` header (seconds). Back off and retry rather than looping immediately.

<Note>
  `otp_send_failed`, `template_create_failed`, and `send_failed` wrap the real downstream error text in `message` — read it, since the specific reason (e.g. "Session window closed. Use a template.") is usually more actionable than the generic code.
</Note>
