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

# Conversation Tools

> Reference for the conversation read and write tools an agent key can call over the Sapot MCP server.

The conversation tools let a coding agent triage, read, and act on conversations. They split across two scopes: **`conversations:read`** for listing and reading, **`conversations:write`** for replying and changing state. A key that lacks a scope gets an auth error naming the missing capability — that is an enforced boundary, not a bug.

<Note>
  Conversations are addressed by their **display id** — the per-account number shown in the dashboard URL and UI — **not** the global database id. Every tool below takes `conversation_id` as that display id.
</Note>

<Info>
  Lists page at **25 per page** (`page` is 1-based). Messages are **cursor-paginated by message id** (newest \~20 by default). Counts in list/search/filter results come back under `meta`.
</Info>

## Read tools

Scope: `conversations:read`.

### list\_conversations

List conversations in the account, newest activity first (25 per page).

| Argument        | Type      | Required | Notes                                                                    |
| --------------- | --------- | -------- | ------------------------------------------------------------------------ |
| `status`        | string    | no       | One of `open` (default), `resolved`, `pending`, `snoozed`, `all`.        |
| `assignee_type` | string    | no       | `me`, `unassigned`, `assigned`, or `all`.                                |
| `inbox_id`      | int       | no       | Restrict to a single inbox.                                              |
| `labels`        | string\[] | no       | Conversation label titles; matches conversations having **any** of them. |
| `page`          | int       | no       | 1-based page number (default 1).                                         |

Returns `{data:{meta:{...counts}, payload:[conversation,...]}}`.

### count\_conversations

Return only the conversation counts for a filter, without the conversation list. Use this when you only need totals.

| Argument        | Type      | Required | Notes                                                 |
| --------------- | --------- | -------- | ----------------------------------------------------- |
| `status`        | string    | no       | Same values as `list_conversations` (default `open`). |
| `assignee_type` | string    | no       | `me`, `unassigned`, `assigned`, or `all`.             |
| `inbox_id`      | int       | no       | Restrict to a single inbox.                           |
| `labels`        | string\[] | no       | Conversation label titles; any-match.                 |

Returns `{meta:{mine_count, assigned_count, unassigned_count, all_count}}`.

### search\_conversations

Full-text search conversations by message content.

| Argument | Type   | Required | Notes                            |
| -------- | ------ | -------- | -------------------------------- |
| `query`  | string | yes      | The text to search for.          |
| `page`   | int    | no       | 1-based page number (default 1). |

Returns `{meta:{...counts}, payload:[conversation,...]}`.

### filter\_conversations

Advanced conversation filtering with structured predicates.

| Argument  | Type    | Required | Notes                                  |
| --------- | ------- | -------- | -------------------------------------- |
| `payload` | dict\[] | yes      | List of predicate objects (see below). |
| `page`    | int     | no       | 1-based page number (default 1).       |

Each predicate is shaped like:

```json theme={null}
{ "attribute_key": "status", "filter_operator": "equal_to", "values": ["open"], "query_operator": "and" }
```

Returns `{meta:{...counts}, payload:[conversation,...]}`.

### get\_conversation

Fetch a single conversation by its display id.

| Argument          | Type | Required | Notes                                                         |
| ----------------- | ---- | -------- | ------------------------------------------------------------- |
| `conversation_id` | int  | yes      | The conversation's display id (**not** a global database id). |

Returns the full conversation object — `meta.sender`/`assignee`/`team`, `status`, `labels`, `priority`, and the most recent message.

### get\_conversation\_messages

List messages in a conversation (cursor-paginated by message id, newest \~20 by default).

| Argument                   | Type | Required | Notes                                                                    |
| -------------------------- | ---- | -------- | ------------------------------------------------------------------------ |
| `conversation_id`          | int  | yes      | The conversation display id.                                             |
| `before`                   | int  | no       | Return the \~20 messages **older** than this message id.                 |
| `after`                    | int  | no       | Return up to \~100 messages **newer** than this message id.              |
| `filter_internal_messages` | bool | no       | When true, excludes private notes and activity messages (default false). |

Returns `{meta:{contact, assignee, ...}, payload:[message,...]}`.

## Write tools

Scope: `conversations:write`.

### reply\_to\_conversation

Post a message to a conversation — a customer-visible reply or an internal note.

| Argument          | Type   | Required | Notes                                                                                                                     |
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `conversation_id` | int    | yes      | The conversation display id.                                                                                              |
| `content`         | string | yes      | The message text.                                                                                                         |
| `private`         | bool   | no       | When true, creates an internal note (not sent to the customer) instead of a reply. Default false.                         |
| `message_type`    | string | no       | `outgoing` (default, agent → customer). `incoming` is accepted **only** on API-channel inboxes and is otherwise rejected. |

Returns the created message object.

<Warning>
  `reply_to_conversation` is **customer-visible and immediate** — with `private=false` (the default) the message is sent to the customer the moment the tool succeeds. Show the draft text to the human and reply only when asked. Use `private=true` for an internal note.
</Warning>

### assign\_conversation

Assign a conversation to an agent and/or a team. Provide at least one of `assignee_id` / `team_id`.

| Argument          | Type | Required | Notes                                                        |
| ----------------- | ---- | -------- | ------------------------------------------------------------ |
| `conversation_id` | int  | yes      | The conversation display id.                                 |
| `assignee_id`     | int  | no       | Id of the agent to assign; pass **0** to unassign the agent. |
| `team_id`         | int  | no       | Id of the team to assign; pass **0** to unassign the team.   |

Returns the assigned agent/team object (or `{}` when unassigned). Calling with neither argument raises an error.

### set\_conversation\_status

Resolve, reopen, mark pending, or snooze a conversation.

| Argument          | Type   | Required | Notes                                                                   |
| ----------------- | ------ | -------- | ----------------------------------------------------------------------- |
| `conversation_id` | int    | yes      | The conversation display id.                                            |
| `status`          | string | yes      | One of `resolved`, `open`, `pending`, `snoozed`.                        |
| `snoozed_until`   | string | no       | ISO8601 timestamp; only used (and required) when `status` is `snoozed`. |

Returns `{payload:{success, conversation_id, current_status, snoozed_until}}`.

<Warning>
  `status` is **always required**. Unlike the dashboard's toggle, this tool never infers the target state — sending no status would silently flip open ↔ resolved, so it is mandatory.
</Warning>

### set\_conversation\_priority

Set (or clear) a conversation's priority.

| Argument          | Type           | Required | Notes                                                            |
| ----------------- | -------------- | -------- | ---------------------------------------------------------------- |
| `conversation_id` | int            | yes      | The conversation display id.                                     |
| `priority`        | string \| null | yes      | One of `urgent`, `high`, `medium`, `low`, or `null` to clear it. |

Returns `{}` on success.

### add\_conversation\_labels

Set the labels on a conversation.

| Argument          | Type      | Required | Notes                              |
| ----------------- | --------- | -------- | ---------------------------------- |
| `conversation_id` | int       | yes      | The conversation display id.       |
| `labels`          | string\[] | yes      | The complete list of label titles. |

Returns `{payload:[label,...]}`.

<Warning>
  `add_conversation_labels` **replaces** the conversation's entire label set — it is not additive. To add one label, first read the existing labels (via `get_conversation`) and send the **union**; otherwise you will drop every label not in your list.
</Warning>

## Related

<CardGroup cols={3}>
  <Card title="Authentication & Scopes" icon="key" href="/ai-agents/authentication">
    How agent keys authenticate and the default-deny scope model.
  </Card>

  <Card title="Contact Tools" icon="list" href="/ai-agents/contacts">
    Find, create, update, and label contacts.
  </Card>

  <Card title="REST API" icon="book" href="/api-reference/introduction">
    Call the same endpoints directly with a scoped token.
  </Card>
</CardGroup>
