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

# Conversations and messages

> Read conversations, send messages and hand a conversation between your team and an agent.

A conversation is a customer on one channel. The API calls it a case. A case holds the messages, who answers it now
(an agent or a person) and its status: `open`, `waiting` or `closed`. You can list cases, read one with its messages,
reply as a person, take a case over from the agent, give it back, assign it, route it and close it.

All routes are under `https://api.impellabs.tech/api/v1`. Send `Authorization: Bearer imp_...` on each request. See
[Authentication](https://docs.impellabs.tech/api/authentication.md) and [Conventions](https://docs.impellabs.tech/api/conventions.md) for tokens, errors and ids. The full list
of fields for each route is in the API reference.

## Who can see a conversation

A token acts as the person who made it. That person sees only the cases they may read:

* Cases on a channel they connected.
* Cases on a channel that is shared with them, with their department, or with everyone.
* Cases assigned to them, taken over by them, or routed to their department.

A channel is private to its owner until the owner shares it. Owners and admins get no extra view of the messages on a
private channel. A case you cannot see answers `404` with `There is no conversation like that.` The same answer is
used when the case does not exist, so a response never tells you that a hidden case is there.

## List conversations

`GET /cases`

| Query | Meaning |
| - | - |
| `status` | `open`, `waiting` or `closed`. |
| `channel` | A channel id, or a channel kind. |
| `assignee` | `me`, `none` or a member id. |
| `owner` | The member id of the person who connected the channel. |
| `agent` | An agent id. |
| `contact` | A contact id. |
| `department` | A department id. |
| `q` | Words in the customer name, an address or a message. |
| `unread` | `true` for cases with customer messages not yet read. |
| `offset` | Where the next page starts. |

The reply holds `results` (50 at most), `next_offset` (`null` at the end) and `counts`, the number of cases in each
status under the other filters.

```bash theme={null}
curl "https://api.impellabs.tech/api/v1/cases?status=open&assignee=me" \
  -H "Authorization: Bearer imp_..."
```

```json theme={null}
{
  "results": [
    {
      "id": "3f0c6a52-0b41-4c55-9a7e-5d2f1c9b8a10",
      "status": "open",
      "channel": { "id": "7b1d...", "kind": "whatsapp", "name": "Main line",
                   "owner": { "id": "a2c4...", "name": "Sam Lee" } },
      "contact": { "id": "c91e...", "name": "Dana", "address": "+15550100", "photo": null },
      "assignee": null,
      "agent": { "id": "5e77...", "name": "Front desk" },
      "answering": { "who": "agent", "reason": "" },
      "unread": true,
      "unread_count": 2,
      "last": { "id": "b8d3...", "direction": "in", "author": "customer", "kind": "message",
                "text": "Is the shop open on Sunday?", "at": "2026-01-12T09:30:00+00:00" },
      "window": { "open": true, "closes_at": "2026-01-13T09:30:00+00:00" },
      "opened_at": "2026-01-12T09:30:00+00:00",
      "last_message_at": "2026-01-12T09:30:00+00:00"
    }
  ],
  "next_offset": null,
  "counts": { "open": 1, "waiting": 0, "closed": 4 }
}
```

The example shows the main fields. `answering` says who answers now: `who` is `agent`, `person` or `nobody`, with a `reason`. Each case also carries `thread_of`, `threads`, `taken_over`, `handoff`,
`summary`, `department`, `closed_at`, `close_reason` and `last_inbound_at`.

Times are stored in UTC and written in ISO 8601 with an offset.

## Read one conversation

`GET /cases/{case_id}` returns the case and a page of its messages, oldest first. Use `limit` (up to 200, 50 by
default). To read older messages, send `before` with the `next` value of the last page.

```bash theme={null}
curl "https://api.impellabs.tech/api/v1/cases/3f0c6a52-0b41-4c55-9a7e-5d2f1c9b8a10?limit=20" \
  -H "Authorization: Bearer imp_..."
```

The reply is the case object plus `messages` and `next`. Each message has `id`, `direction` (`in`, `out` or `app`),
`author`, `kind`, `text`, `emoji`, `media`, `data`, `reply_to`, `member_id`, `agent_id`, `run_id`, `case_id`,
`channel` and `at`. The case also has `reply_waits`: the approval that holds a drafted reply, or `null`.

## Send a message

`POST /cases/{case_id}/messages` sends a message to the customer as the person who owns the token. It also takes the
case over, so the agent stops answering until you give the case back.

| Field | Meaning |
| - | - |
| `text` | The words, 4096 characters at most. |
| `template` | `{ "name", "language", "params", "preview" }`. Use a template when the reply window is closed. Send it alone, with no `text` and no `file_ids`. |
| `file_ids` | Ids of files you sent to `POST /files`. The `text` goes as the caption of the first file. |
| `reply_to` | The id of a message in this conversation to quote. |
| `thread` | The id of a thread case, when the conversation has threads on other channels. |
| `key` | A key you choose. A repeat of the same key does not send twice. |

Send at least one of `text`, `template` or `file_ids`.

```bash theme={null}
curl -X POST "https://api.impellabs.tech/api/v1/cases/3f0c6a52-0b41-4c55-9a7e-5d2f1c9b8a10/messages" \
  -H "Authorization: Bearer imp_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "Yes, we open at 10:00 on Sunday.", "key": "sunday-hours-1"}'
```

The reply is `201`: the first message, with all sent messages in `messages`.

```json theme={null}
{
  "id": "e41a...",
  "direction": "out",
  "author": "member",
  "kind": "message",
  "text": "Yes, we open at 10:00 on Sunday.",
  "at": "2026-01-12T09:41:00+00:00",
  "messages": [ { "id": "e41a...", "text": "Yes, we open at 10:00 on Sunday." } ]
}
```

Errors:

| Status | Cause |
| - | - |
| `400` | No `text`, `template` or `file_ids`; `text` is too long; a file is not in the library; or the send checks refused the message. A refused message returns `detail` and `check`. |
| `400` | The conversation is closed. Open it again first. |
| `404` | You cannot see the case. |

## Take over, hand back, assign, route

Each action below is a `POST` to `/cases/{case_id}/<action>`. Each returns the updated case.

| Action | Body | Effect |
| - | - | - |
| `take-over` | none | You answer. The agent stops. |
| `hand-back` | none | The agent answers again. The summary of the pause is saved on the case. |
| `assign` | `{ "member_id": "<id>" or null, "note"? }` | The case goes to another person. Their agent answers from then on. The `note` has 300 characters at most. |
| `route` | `{ "department_id" or "member_id" or "agent_id", "reason" }` | The case moves. Send exactly one target. |
| `close` | none | The case closes. |
| `reopen` | none | A closed case opens again. A case that cannot open again answers `409`. |
| `read` | none | Marks the case, and its threads, as read. |
| `clear` | none | Closes the case as a test. Nothing from it is learned. The next message starts a new case. |
| `suggestions` | none | The case's agent drafts replies for you. Nothing is sent. Returns `{ "replies": [...] }`. |

`GET /cases/routing-options` lists where a case can go: `{ "options": [ { "kind", "id", "name" } ] }` with `kind`
`department` or `agent`.

```bash theme={null}
curl -X POST "https://api.impellabs.tech/api/v1/cases/3f0c6a52-0b41-4c55-9a7e-5d2f1c9b8a10/take-over" \
  -H "Authorization: Bearer imp_..."
```

## Start a conversation

`POST /cases` with `{ "contact_id", "channel_id" }` opens a conversation with a contact, or returns the open one on
that channel. It answers `201` for a new case and `200` for an existing case. It sends no message. Send the first
message with `POST /cases/{case_id}/messages`; on a channel with a closed window, use a template.

| Status | Cause |
| - | - |
| `400` | The contact asked not to be contacted, the contact has no address on this channel, or the channel does not exist. |
| `403` | You cannot read this channel. |
| `404` | You cannot see the contact. |

## Settings, handoff rules and saved replies

These routes configure how conversations run for the workspace. They are in the API reference:

* `GET` and `PATCH /cases/settings`: the quiet-close time and the time a handoff waits before the agent tells the
  customer a person will follow up.
* `GET` and `POST /cases/handoff-rules`, and `PATCH` and `DELETE /cases/handoff-rules/{rule_id}`: who takes a
  customer the agent hands on.
* `GET` and `POST /cases/saved-replies`, `PATCH` and `DELETE /cases/saved-replies/{reply_id}`, and
  `POST /cases/saved-replies/{reply_id}/use`: lines your team sends often.


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