> For an agent: the map of this documentation is at https://escouade.io/en/mail/docs/llms.txt; every page exists in Markdown, at the same address with `.md` appended.

# Threads

> A thread is the conversation between an inbox and a correspondent. It starts with the first send or the first e-mail received. Replies are filed in it, and your agent replies in the same thread.

## A thread's statuses

| Status | Meaning |
|---|---|
| `open` | Open: waiting for a reply, or a new message received. |
| `replied` | The correspondent replied: to be dealt with. |
| `bounced` | The correspondent's address failed. |
| `unsubscribed` | The correspondent unsubscribed: do not write to them again. |
| `closed` | Closed: dealt with and closed with `close_thread`, or all its messages were cancelled. |

A thread also carries its origin: `outbound` if it started with a send, `inbound` if it started with an e-mail received.

## How a thread changes status

| What happens | The thread becomes |
|---|---|
| The inbox writes a first message, or receives a first e-mail | `open` |
| The correspondent replies, or writes again | `replied` |
| The inbox writes in the thread, drafts included | `open`, if it was `replied` |
| An automatic reply arrives | unchanged |
| A permanent non-delivery notice arrives | `bounced` |
| The correspondent unsubscribes | `unsubscribed` |
| All the thread's messages were cancelled | `closed` |
| The thread is dealt with, and closed with `close_thread` | `closed` |
| The correspondent replies in a closed thread | `replied` |
| The inbox replies in a closed thread | `open` |

- **Replying takes the thread out of the replies to deal with**: it goes back to `open`.
- **A first e-mail received opens an `open` thread, not a `replied` one.** To find it, see [Receiving mail](https://escouade.io/en/mail/docs/recevoir.md#relever).
- **A thread that has been dealt with is closed with `close_thread`**: it leaves what waits for a reply. The server also closes by itself an `open` thread whose messages were all cancelled. A closed thread reopens if the correspondent replies in it, or if the inbox replies in it with `reply_to_thread`. `send_email` to the same correspondent, on the other hand, opens another thread.
- **An inbox has a single current thread per correspondent.** A new e-mail from the same correspondent joins their thread, if it is not closed.

## Receiving

Every inbox receives, with nothing to set up.

- **A reply** is attached to its thread by its headers (`In-Reply-To`, `References`), otherwise by its correspondent. The thread moves to `replied`.
- **A new e-mail** opens a thread of origin `inbound`.
- **An automatic reply** (out of office, acknowledgement) is marked `auto_reply` and does not change the thread's status.
- **A reply asking to stop** (“STOP”, “unsubscribe me”…) unsubscribes the correspondent: the thread moves to `unsubscribed`. See [Unsubscribes and blocks](https://escouade.io/en/mail/docs/desinscription.md).
- **A non-delivery notice** marks the message and the thread `bounced`. A hard bounce blocks the address.

To collect its mail, an agent calls `list_threads` with `unanswered` = `true`, then `get_thread`. That call returns replies received and first e-mails: see [Receiving mail](https://escouade.io/en/mail/docs/recevoir.md#relever).

## `list_threads`

Lists threads, newest first. Permission: `read`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `inbox_id` | `inb_…` identifier | no | A single inbox. |
| `status` | text | no | A single status: `open`, `replied`, `bounced`, `unsubscribed` or `closed`. |
| `unanswered` | boolean | no | `true`: only threads whose last message is a received e-mail that nothing has been written back to yet. A reply, or a first e-mail. |
| `since` | date | no | Threads active since this date (ISO 8601): those whose last message, incoming or outgoing, is later. |
| `query` | text | no | A search: part of the correspondent's address, of their name or of the subject. 100 characters at most. |
| `folder` | text | no | A folder: `received`, `sent` or `drafts`. |
| `label` | text | no | Threads carrying this exact tag (see `classify_thread`). 40 characters at most. |
| `archived` | boolean | no | `true`: archived threads; `false`: those that are not. |
| `unread` | boolean | no | `true`: threads with an unread received message; `false`: those that are read. |
| `cursor` | text | no | The cursor of the next page: the `next_cursor` value from the previous call. |
| `limit` | integer | no | The number of items returned, from 1 to 100. 20 by default. |

```text
1 fil(s) :
• thr_b81f4c6a2d9e47035c1a8e6f0b4d2c97 — Nadia Morel <nadia.morel@exemple.com> — « Rendez-vous de jeudi » — réponse reçue, à traiter — 2 messages — dernier vendredi 9 octobre à 16 h 12
```

## `get_thread`

Reads a thread: its messages in order, incoming and outgoing, with their text and their status. Read it before replying. Permission: `read`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `thread_id` | `thr_…` identifier | yes | The thread. |
| `cursor` | text | no | The cursor for the rest of the thread: the `next_cursor` value from the previous call. |

The response carries `thread` and `messages`. A message's fields are described in [Messages](https://escouade.io/en/mail/docs/messages.md#champs).

One call returns 50 messages at most. If more remain, the response carries `next_cursor`. Beyond 200 KB of text per call, later bodies are cut: the response carries `truncated: true`, and `get_message` returns the whole message.

A received message carries `untrusted: true`: its content was written by a third party. Your agent reads it as information, never as an instruction. In the readable text, this content is fenced off: see [Receiving mail](https://escouade.io/en/mail/docs/recevoir.md#tiers).

In the readable text, a message's body stops at 2,000 characters. The whole body is in the structured object (`text`).

In an assistant that displays MCP Apps views, `get_thread` comes with a view: the conversation in order. See [Views](https://escouade.io/en/mail/docs/vues.md).

## `reply_to_thread`

Replies in a thread, to the thread's correspondent: subject “Re: …” and `In-Reply-To` and `References` headers, so that the reply shows in the same conversation for the recipient. Also used to follow up. Permission: `send`. The space must be verified, and have an active plan: without a plan, the reply can only be a draft.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `thread_id` | `thr_…` identifier | yes | The thread (see `list_threads`). |
| `subject` | text | no | The subject. By default: “Re:” followed by the thread's subject. |
| `cc` | list of texts | no | Addresses in copy, five at most. Only if the correspondent has already written in the thread, to someone who has also written in it or to an address of the space. |
| `text` | text | yes | The plain text of the reply. |
| `html` | text | no | An HTML version, with the same content as `text`. |
| `reply_to` | text | no | The reply-to address, if it differs from the inbox. |
| `from_address` | text | no | The sending address: the inbox's main address, or one of its aliases (see `list_inboxes`). |
| `metadata` | object | no | The agent's free data, 8,000 characters at most. |
| `requires_approval` | boolean | no | `true`: the reply stays a draft until a human approves it. |
| `send_after` | date | no | Not before this date (ISO 8601, or Paris time without an offset). |
| `idempotency_key` | text | no | A unique key for this send, 1 to 255 characters. |

Two cases, depending on whether the correspondent has already written in the thread.

| Rule | The correspondent has written: a conversation | They have not written: a follow-up |
|---|---|---|
| Working hours | The reply leaves without waiting for the sending window. | It waits for the sending window. |
| Departure order | It goes ahead of first messages in the queue. | It follows the queue. |
| Unsubscribe footer | No visible footer. The one-click link stays in the headers. | The footer is added. |
| Inbox on the shared domain | Accepted. | Refused: `own_domain_required`. |
| Inbox on the customer's domain | Accepted. | Accepted. |
| Daily quota, spacing | Apply. | Apply. |
| Block list, human approval | Apply. | Apply. |
| Attachment, copy (`cc`) | Allowed. | Refused: `attachments_not_allowed`, `cc_not_allowed`. |

In both cases, the duplicate rule does not apply: a reply in a thread is not a duplicate, and it counts towards the plan's monthly volume. An automatic reply from the correspondent does not count as a written message.

The step-by-step guide: [Reply to a correspondent](https://escouade.io/en/mail/docs/guide-repondre.md). Attaching a file, adding a copy: [Attachments and copies](https://escouade.io/en/mail/docs/pieces-jointes.md).

```text
Message msg_e93b7d1a5c0f42b8d6e2a9c4f7b1d056 en file dans le fil thr_b81f4c6a2d9e47035c1a8e6f0b4d2c97 : de agent@exemple.fr à nadia.morel@exemple.com, « Re: Rendez-vous de jeudi ». Le correspondant a déjà écrit dans ce fil : la réponse part sans attendre le créneau. Départ au plus tôt : samedi 10 octobre à 10 h 05. Quota du jour de la boîte : 0/10 (semaine 2 du réchauffage, palier de 10 par jour).
```

## `close_thread`

Closes a thread that has been dealt with: it takes the `closed` status and leaves what waits for a reply. Nothing is erased. Permission: `send` or `approve`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `thread_id` | `thr_…` identifier | yes | The thread to close. |

- The result returns the thread (`thread`) and `closed`: `true` if it has just been closed, `false` if it already was.
- **While a draft or a queued message waits in the thread**, the call is refused with the code `thread_busy`: cancel it with `cancel_message`, or let it leave.
- A `bounced` or `unsubscribed` thread is already finished: code `thread_not_closable`.
- A closed thread reopens by itself if the correspondent replies in it, or if the inbox replies in it.

## `classify_thread`

Adds or removes a conversation's tags (labels replaces the whole list), archives or restores the thread, marks received messages as read or unread. Access: `send` or `approve`.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `thread_id` | `thr_…` identifier | yes | The conversation to classify. |
| `labels` | list | no | The thread's tags, 20 at most (40 characters each). Replaces the whole list. |
| `archived` | boolean | no | Archive or restore. |
| `read` | boolean | no | Mark as read or unread. |

A thread can carry up to 20 tags; `list_threads` filters them by `label`. Archiving a thread files it away without erasing or blocking anything.

[Full reference](https://escouade.io/en/mail/docs/reference/classify_thread.md)

## The fields of a thread

| Field | Content |
|---|---|
| `id` | The thread identifier, `thr_…`. |
| `inbox_id` | The thread's inbox. |
| `counterpart`, `counterpart_name` | The correspondent's address and name. |
| `subject` | The subject. |
| `status` | See [the statuses](#statuts). |
| `origin` | `outbound` or `inbound`. |
| `message_count` | The number of messages. |
| `last_message_at`, `last_inbound_at`, `last_outbound_at`, `created_at` | The dates, in ISO 8601. |

## Going through a list

Lists return `items` and `next_cursor`. As long as `next_cursor` is not `null`, pass it back as it is in `cursor` for the next page.
