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

# Errors and rate limits

> The Dialbird API error envelope, error codes, and rate limiting headers.

## Error envelope

Every error shares one envelope:

```json theme={null}
{
  "error": {
    "code": "not_found",
    "message": "Contact not found.",
    "field": "to",
    "request_id": "req_8f3c2c1d"
  }
}
```

* `code` — a stable, machine-readable identifier. Branch on this, not on the message.
* `message` — a human-readable description. Subject to change; don't parse it.
* `field` — present **only** for validation errors tied to a specific input field.
* `request_id` — the same value as the `X-Request-Id` response header. Include it when contacting support.

## Error codes

| Code                      | Typical status | Meaning                                                |
| ------------------------- | -------------- | ------------------------------------------------------ |
| `validation_error`        | `400`          | The request body failed validation.                    |
| `invalid_phone_number`    | `400`          | A phone number was not valid E.164.                    |
| `invalid_event_type`      | `400`          | Unknown webhook event type.                            |
| `missing_token`           | `401`          | No `Authorization` header was sent.                    |
| `invalid_token`           | `401`          | The token is malformed or failed verification.         |
| `expired_token`           | `401`          | The token is past its expiry.                          |
| `insufficient_scope`      | `403`          | The token lacks the required scope.                    |
| `forbidden`               | `403`          | The caller may not access the resource.                |
| `business_suspended`      | `403`          | The business is suspended or not associated.           |
| `not_found`               | `404`          | The resource does not exist.                           |
| `conflict`                | `409`          | The request conflicts with current state.              |
| `idempotency_key_reused`  | `409`          | An `Idempotency-Key` was reused with a different body. |
| `idempotency_in_progress` | `409`          | A prior request with the same key is still in flight.  |
| `gone`                    | `410`          | The resource is no longer available.                   |
| `unprocessable_entity`    | `422`          | The request was understood but cannot be processed.    |
| `rate_limited`            | `429`          | The rate limit was exceeded.                           |
| `internal_error`          | `500`          | An unexpected server error.                            |

## Rate limiting

Authenticated requests are limited per `(business, oauth_client)` pair. Anonymous requests (only `GET /health`) are limited per IP.

Every response includes:

| Header                  | Meaning                                    |
| ----------------------- | ------------------------------------------ |
| `X-RateLimit-Limit`     | Requests allowed in the current window.    |
| `X-RateLimit-Remaining` | Requests left in the window.               |
| `X-RateLimit-Reset`     | Unix epoch seconds when the window resets. |

A `429 rate_limited` response additionally carries a `Retry-After` header (seconds). Honor it: wait the indicated time before retrying, and prefer exponential backoff for repeated `429`s.
