> This page is for Omni Channel.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.hams.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.hams.ai/_mcp/server.

# Errors

> The error codes of the developer API, the body of each error, and what to do about it.

The developer API has 2 groups of endpoints, and their error bodies are not the same:

* The send endpoint (`POST .../developer/messages`), the pre-call context endpoint (`GET .../developer/precall_context`), and the phone number contact endpoints. Their errors have a stable `error` code and a `message`.
* The conversation and contact endpoints. Their errors have a text in `error`, or a `message` and a list of `attributes`.

## Send and pre-call context errors

The send and pre-call context endpoints return this body for a `422` and a `404`:

```json
{
  "error": "outside_messaging_window",
  "message": "The customer service window is closed for this conversation. Send a template message instead."
}
```

`error` is a stable code for a program. Branch on it. `message` is for a person who reads your logs. It can change at any time.

### 422 Unprocessable Entity

The API understood the request but cannot do it.

| Code                       | Cause                                                                                                                                                                                  | What to do                                                                                               |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `phone_number_required`    | The request to a phone number contact endpoint had no `phone_number`.                                                                                                                  | Send `phone_number`. See [Contacts](/omni-channel/api/contacts).                                         |
| `validation_failed`        | The create or update by phone number request has a field that is not valid. For example, another contact has the email address. `message` gives the reason.                            | Correct the field. The API changed nothing.                                                              |
| `identifier_required`      | The request had none of `conversation_id`, `contact_id`, or `phone_number`.                                                                                                            | Send exactly 1 of them.                                                                                  |
| `inbox_id_required`        | The send request had no `conversation_id` and no `inbox_id`.                                                                                                                           | Add `inbox_id`.                                                                                          |
| `content_required`         | `content` and `template_params` were both empty. Or `content` was empty and the inbox has no approved template with that `name` and `language`. A Twilio inbox always needs `content`. | Send `content`, or fix the template `name` and `language`. See [Templates](/omni-channel/api/templates). |
| `invalid_phone_number`     | The number is not in E.164 format.                                                                                                                                                     | Send a `+`, the country code, then digits only.                                                          |
| `unsupported_channel`      | The send request used `phone_number` on an inbox that has no phone numbers.                                                                                                            | Use `phone_number` only on WhatsApp, Twilio, and SMS inboxes. Use `contact_id` on other inboxes.         |
| `contact_not_messageable`  | The contact has no address that the channel of the inbox can reach.                                                                                                                    | Add a phone number to the contact, or an email address for an email inbox.                               |
| `outside_messaging_window` | The request had plain `content` and the service window has closed, or the customer never sent a message.                                                                               | Send a template. See [Templates](/omni-channel/api/templates).                                           |
| `contact_conflict`         | The API could not save the `contact` fields of a new contact. Usually another contact has the email address already. An email address with a wrong format also causes this error.      | Send the request without `contact.email`, or correct the address. `message` names the field.             |

> **Note**
>
> `contact_conflict` is strict on purpose. If the API used the contact that has that email address, it would send your message to the phone number of that contact. The API rejects the request instead.

A send can also fail with a `422` that has a `message` and `attributes` and no `error` code. An example is a send with `template_params` to the Phone inbox, which cannot deliver a message. A send with plain `content` to the Phone inbox fails first with `outside_messaging_window`. See [Conversation and contact errors](#conversation-and-contact-errors).

The phone number contact endpoints (`GET .../developer/contacts/by_phone` and `POST .../developer/contacts/create_or_update`) return the same body for a `422`. They return `404` with the text body in [Conversation and contact errors](#conversation-and-contact-errors).

### 404 Not Found

```json
{ "error": "not_found", "message": "Couldn't find Conversation" }
```

The `conversation_id`, `contact_id`, `inbox_id`, or `phone_number` is not in this account. An id from another account gives the same `404`. The API never tells you that an id exists in another account.

## Conversation and contact errors

The conversation, label, assignment, and contact endpoints return these bodies.

A `404` has 1 fixed text:

```json
{ "error": "Resource could not be found" }
```

A `422` for a field that is not valid has a `message` and the names of the fields. An example is an email address that another contact has:

```json
{ "message": "Email has already been taken", "attributes": ["email"] }
```

A `422` for a filter that is not valid, or for a message that the API cannot create, has the reason in `error`:

```json
{ "error": "Query operator must be either \"AND\" or \"OR\"." }
```

## 401 Unauthorized

The API returns `401` for all authentication and account failures. It has no separate `403`. When the request has a token, the body has only the `error` field:

```json
{ "error": "Invalid Access Token" }
```

| `error`                                                    | Cause                                                                            |
| ---------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `Invalid Access Token`                                     | The `api_access_token` header has a token that does not exist, or a deleted key. |
| `Key is not authorized to access this account`             | The key belongs to another account than the `account_id` in the path.            |
| `Access to this endpoint is not authorized for this token` | The token is not an API key, for example the personal access token of a user.    |
| `You are not authorized to access this account`            | The token is the token of a user who is not a member of the account in the path. |
| `Account is suspended`                                     | The account is not active.                                                       |

A request with no `api_access_token` header also returns `401`. Its body is a sign-in error with a different shape, not an `error` field.

> **Note**
>
> A key reaches the whole account that created it: every inbox, conversation, and contact. It reaches nothing in other accounts.

Keep a key as secret as a password. Create 1 key for each integration, so that you can revoke 1 key and keep the others.

## 429 Too Many Requests

By default, a key can send 300 requests in 1 minute. The key sent more than its limit. Wait until the next minute. Then try again.

The rate limit applies before the application gets the request. The body is therefore plain text, not JSON:

```
Retry later
```

## Related

* [Quickstart](/omni-channel/api/quickstart)
* [Messages](/omni-channel/api/messages)
* [Templates](/omni-channel/api/templates)
* [Pre-call context](/omni-channel/api/precall-context)