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

# Errors

> Every error code the Messaging API returns, with recommended actions.

All errors return a structured JSON envelope:

```json theme={null}
{
  "success": false,
  "error": {
    "status": 400,
    "code": 1001,
    "message": "Missing required field: phone",
    "retryable": false
  },
  "trace_id": "a1b2c3d4e5f6"
}
```

Some codes attach extra context fields next to `code`/`message`/`retryable`:

| Field              | When                                                                                                                           |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `retry_after`      | Rate-limit and capacity errors (`1007`, `4002`). Seconds to wait; also mirrored in the `Retry-After` header.                   |
| `attachment_index` | `1011`/`1002` / `5xxx` raised by a specific entry in an `attachments` or `images` array. 0-based index of the offending entry. |
| `allowed_effects`  | `4098 UNKNOWN_EFFECT`. The current list of valid `effect` values.                                                              |

<ResponseField name="error.status" type="integer">
  The HTTP status code, repeated in the body for convenience. Note that the HTTP status is **not** derivable from the `code` range — e.g. `1011` is a `1xxx` code returned with HTTP `409`.
</ResponseField>

<ResponseField name="error.code" type="integer">
  A stable integer — build your error-handling logic against it. Never changes for a given failure class.
</ResponseField>

<ResponseField name="error.message" type="string">
  Human-readable detail. May change between releases — do not branch on it. Internal delivery names, env var names, raw database errors, and crypto-step detail are deliberately **not** included; the full detail is logged server-side under `trace_id`.
</ResponseField>

<ResponseField name="error.retryable" type="boolean">
  **Always present and authoritative — branch on this, not on the code or HTTP status.** `true` for `3xxx` server/infra errors, most `4xxx` delivery errors, and `1007` rate-limiting; `false` for everything else (including `4098 UNKNOWN_EFFECT` and `4099 NOT_SUPPORTED`). A few routes override the default — notably `3003 PROVIDER_NOT_CONFIGURED` is sent with `retryable: false` because a missing server-side key won't fix itself. Codes can shift between retryable and not at the route level; always trust the field, never re-derive it.
</ResponseField>

<ResponseField name="error.retry_after" type="integer" optional>
  Present on rate-limited / capacity responses. Seconds to wait before retrying; mirrored in the `Retry-After` header.
</ResponseField>

<ResponseField name="trace_id" type="string">
  Correlation id for this response, also returned in the `X-Trace-ID` header (on both success and error responses). Quote it when contacting support.
</ResponseField>

The `code` field is a stable integer — build your error-handling logic against it. The `message` is human-readable and may change between releases.

<Note>
  Every error path across all four customer-facing API surfaces — `/api/v1/*`, `/api/salesforce/*`, `/api/hubspot/*`, `/api/slack/*` — carries a numeric `code`, a human `message`, a `retryable` boolean, and a `trace_id`. Auth and signature failures return **distinct** codes (`2012 AUTH_MISSING`, `2004 AUTH_INVALID`, `2013 AUTH_TIMESTAMP_SKEW`) rather than one opaque "unauthorized".
</Note>

<Note>
  Some endpoints keep a **flat** shape rather than the `{success,error,trace_id}` envelope, but carry the same fields. `POST /api/v1/send` returns `status: "failed"` (or a flat object) with top-level `code`, `message`, `retryable`, and `trace_id`. `/api/salesforce/*` keeps an `{ ok: false, ... }` shape (the Apex client branches on `ok`) with `code` / `message` / `retryable` / `trace_id` added alongside. `/api/hubspot/*` webhook and workflow routes return HTTP `200` for logical failures (HubSpot's Events API contract) with the failure detail in `{ok:false,...}` / `outputFields:{ok:false,...}`. In every case the numeric `code` scheme is the one documented here, so error-handling logic keyed on `code` works across all shapes. See [Sending → Delivery failures](/api/sending#delivery-failures).
</Note>

***

The numeric `code` is the stable contract. The HTTP status is chosen per-route and is **not** derivable from the code range. The `Retryable` column below is the default `error.retryable` value for that code; a route may override it (see `3003`).

## 1xxx — request errors

Fix the request before retrying.

| Code | HTTP | Retryable | Meaning                                                                                                                                                 | Action                                                                                                                                                                                                           |
| ---- | ---- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1001 | 400  | No        | Missing required field                                                                                                                                  | Check the request body for the named field.                                                                                                                                                                      |
| 1002 | 400  | No        | Invalid field value — also returned for malformed `attachments` arrays                                                                                  | Correct the field to a valid value.                                                                                                                                                                              |
| 1003 | 400  | No        | Invalid phone number                                                                                                                                    | Use E.164 format, e.g. `+15555550100`.                                                                                                                                                                           |
| 1004 | 400  | No        | Invalid JSON                                                                                                                                            | Check `content-type: application/json` and serialization.                                                                                                                                                        |
| 1005 | 400  | No        | First message in a new chat cannot contain URLs in text parts or legacy `link` parts                                                                    | Use a `rich_link` part for previewed URLs, or send a URL-free first text and add links in a follow-up.                                                                                                           |
| 1006 | 400  | No        | Too many parts in a single message                                                                                                                      | Reduce to 10 parts or fewer per message.                                                                                                                                                                         |
| 1007 | 429  | **Yes**   | Rate limited                                                                                                                                            | Back off and retry; respect `retry_after` / `Retry-After`.                                                                                                                                                       |
| 1008 | 400  | No        | Consecutive text parts not permitted                                                                                                                    | Merge adjacent text parts into one.                                                                                                                                                                              |
| 1009 | 400  | No        | Text too long                                                                                                                                           | Shorten the text or split into multiple parts.                                                                                                                                                                   |
| 1010 | 400  | No        | Unsupported messaging service                                                                                                                           | Only `imessage` is supported in v1.                                                                                                                                                                              |
| 1011 | 409  | No        | Resource kind mismatch — wrong resource type for the operation, **or an attachment ref mixes `url` and `attachment_id`, or is missing `attachment_id`** | Address the operation at a resource of the correct type. For attachment errors the response includes `attachment_index`; the message hints "register via POST /api/v1/attachments first" when the id is missing. |
| 1012 | 409  | No        | Resource state invalid — the resource exists but is not in a usable state                                                                               | Wait for / move the resource into a valid state before retrying.                                                                                                                                                 |
| 1013 | 409  | No        | Contact has no usable phone number                                                                                                                      | The addressed contact/record carries no phone; enrich it or pick another.                                                                                                                                        |

## 2xxx — auth / resource errors

Fix credentials or the target before retrying.

| Code | HTTP | Retryable | Meaning                                                                                                           | Action                                                                                           |
| ---- | ---- | --------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| 2001 | 404  | No        | Tenant not found                                                                                                  | Verify `x-chert-tenant` matches your registration response.                                      |
| 2002 | 404  | No        | Chat not found                                                                                                    | Verify the chat id belongs to your tenant.                                                       |
| 2003 | 404  | No        | Message not found                                                                                                 | Verify the message id belongs to your tenant.                                                    |
| 2004 | 401  | No        | Invalid auth — credentials supplied but wrong (bad signature / bad token)                                         | Re-sign with the current timestamp. See [Authentication](/api/authentication).                   |
| 2005 | 403  | No        | Forbidden                                                                                                         | Tenant suspended or you lack permissions for this resource. Contact your Chert administrator.    |
| 2006 | 400  | No        | Phone number not assigned to your tenant                                                                          | Use a number from `GET /api/v1/phone-numbers`.                                                   |
| 2007 | 403  | No        | Email not verified                                                                                                | Click the link in the verification email sent at registration.                                   |
| 2008 | 402  | No        | Account limit reached                                                                                             | Contact your Chert administrator before retrying.                                                |
| 2010 | 403  | No        | Integration not connected — a required OAuth integration is not linked                                            | Connect the integration (Salesforce / HubSpot / Slack), then retry.                              |
| 2011 | 404  | No        | Resource not found — a generically addressed resource is missing (icp / list / sheet / sequence / lead / message) | Verify the id belongs to your tenant.                                                            |
| 2012 | 401  | No        | Auth missing — no credentials were supplied at all                                                                | Add the `x-chert-signature` header (or bearer token). See [Authentication](/api/authentication). |
| 2013 | 401  | No        | Auth timestamp skew — the signature timestamp is outside the allowed clock skew                                   | Fix your system clock and re-sign with a fresh timestamp.                                        |
| 2014 | 400  | No        | OAuth state invalid — the OAuth callback `state` is missing, expired, or mismatched                               | Restart the OAuth flow from the beginning.                                                       |

## 3xxx — server / infrastructure errors

Retryable with exponential backoff — **except `3003`**.

| Code | HTTP      | Retryable | Meaning                         | Action                                                                                             |
| ---- | --------- | --------- | ------------------------------- | -------------------------------------------------------------------------------------------------- |
| 3001 | 500       | **Yes**   | Internal server error           | Retry with exponential backoff. Contact support if persistent.                                     |
| 3002 | 503       | **Yes**   | Delivery unavailable            | Chert delivery infrastructure is unreachable. Retry after a short delay.                           |
| 3003 | 500 / 503 | **No**    | Delivery service not configured | Not retryable despite the `3xxx` range — the response carries `retryable: false`. Contact support. |
| 3004 | 502       | **Yes**   | Delivery service error          | Retry after a short delay; the dependency may be transiently failing.                              |

## 4xxx — delivery errors

Retryable except `4099`.

| Code | HTTP      | Retryable | Meaning                                                     | Action                                                                                                                                                                                                                                                                        |
| ---- | --------- | --------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 4001 | 502       | **Yes**   | Delivery failed                                             | The message was dispatched but the downstream rejected it. Check `message` (or top-level `reason` on the `/api/v1/send` flat response).                                                                                                                                       |
| 4002 | 503       | **Yes**   | All available phone lines are saturated for this recipient  | Retry later. The response includes diagnostic detail; healthy lines free up at the top of the next hour.                                                                                                                                                                      |
| 4098 | 400       | No        | Unknown effect — the `effect` value is not in the whitelist | Response carries `allowed_effects: [...]` with the current valid set. Pick one of those values, or omit `effect`.                                                                                                                                                             |
| 4099 | 422 / 501 | No        | Feature not supported in this context                       | Returned when a route, line, or request shape does not support the requested feature, including group-chat endpoints missing chat key / pinned line and `/messages/{id}/edit` past the 15-min window or on lines without edit support. Status varies by route. Not retryable. |

## 5xxx — attachment errors

Fix the request before retrying.

| Code | HTTP | Retryable | Meaning                     | Action                                                                       |
| ---- | ---- | --------- | --------------------------- | ---------------------------------------------------------------------------- |
| 5001 | 404  | No        | Attachment not found        | Verify the `attachment_id` belongs to your tenant.                           |
| 5002 | 400  | No        | Attachment too large        | Reduce file size below the limit.                                            |
| 5003 | 400  | No        | Unsupported MIME type       | Check the supported types list for your tier.                                |
| 5004 | 400  | No        | Attachment not yet uploaded | Complete the PUT upload step before referencing the attachment in a message. |

***

## Error envelopes — examples

```json 401 auth_missing theme={null}
{
  "success": false,
  "error": {
    "status": 401,
    "code": 2012,
    "message": "No credentials supplied",
    "retryable": false
  },
  "trace_id": "9f8e7d6c"
}
```

```json 502 delivery_service_error theme={null}
{
  "success": false,
  "error": {
    "status": 502,
    "code": 3004,
    "message": "A delivery service returned an error",
    "retryable": true
  },
  "trace_id": "b2c3d4e5"
}
```

```json 409 attachment_ref_invalid theme={null}
{
  "success": false,
  "error": {
    "status": 409,
    "code": 1011,
    "message": "Attachment ref missing attachment_id — register via POST /api/v1/attachments first",
    "retryable": false,
    "attachment_index": 2
  },
  "trace_id": "c3d4e5f6"
}
```

```json 400 unknown_effect theme={null}
{
  "success": false,
  "error": {
    "status": 400,
    "code": 4098,
    "message": "Unknown effect: 'fireworks'",
    "retryable": false,
    "allowed_effects": ["celebration", "balloons", "confetti", "fireball", "heart", "laser", "lasers", "shootingstar", "sparkles", "spotlight"]
  },
  "trace_id": "d4e5f6a7"
}
```

***

## Retry guidance

`error.retryable` is the authoritative signal — it is **always present** on every error response, and clients should branch on it directly rather than re-deriving retryability from the code range or HTTP status. The table below explains the strategy per class.

| Class                          | `retryable` | Strategy                                                                              |
| ------------------------------ | ----------- | ------------------------------------------------------------------------------------- |
| `1xxx` except `1007` (400/409) | `false`     | Fix the request and resubmit.                                                         |
| `1007` (429)                   | `true`      | Respect `retry_after` / `Retry-After`. Exponential backoff otherwise.                 |
| `2xxx` (401/402/403/404)       | `false`     | Fix credentials, account state, integration connection, or target id — then resubmit. |
| `3001` (500), `3002` (503)     | `true`      | Exponential backoff: 1s, 2s, 4s.                                                      |
| `3003` (500/503)               | `false`     | Server-side config gap — retrying won't help. Contact support.                        |
| `3004` (502)                   | `true`      | Delivery service failed transiently. Short delay, then retry.                         |
| `4001` (502), `4002` (503)     | `true`      | Re-attempt after a short delay; check `message` for the reason.                       |
| `4098` (400)                   | `false`     | Pick a value from `allowed_effects` in the response, or omit the field.               |
| `4099` (422/501)               | `false`     | Feature not live in this context; do not retry.                                       |
| `5xxx` (400/404)               | `false`     | Fix the attachment reference and resubmit.                                            |

Use `idempotency_key` on `POST /api/v1/send` and `POST /api/v1/chats` to make retries safe.

## See also

* [Authentication](/api/authentication)
* [Sending](/api/sending)
* [Create a chat](/api/chats/create)
