> Pour un agent : la carte de la documentation d'Escouade Mail est sur https://escouade.io/mail/docs/llms.txt ; chaque page existe en Markdown, à la même adresse suffixée `.md`.

# Fils

> Un fil est la conversation entre une boîte et un correspondant. Il naît au premier envoi ou au premier e-mail reçu. Les réponses s'y rangent, et votre agent répond dans le même fil.

## Les statuts d'un fil

| Statut | Sens |
|---|---|
| `open` | Ouvert : en attente de réponse, ou nouveau message reçu. |
| `replied` | Le correspondant a répondu : à traiter. |
| `bounced` | L'adresse du correspondant est en échec. |
| `unsubscribed` | Le correspondant s'est désinscrit : ne plus lui écrire. |
| `closed` | Clos : traité et clos avec `close_thread`, ou tous ses messages ont été annulés. |

Un fil porte aussi son origine : `outbound` s'il a commencé par un envoi, `inbound` s'il a commencé par un e-mail reçu.

## Comment un fil change de statut

| Ce qui se passe | Le fil devient |
|---|---|
| La boîte écrit un premier message, ou reçoit un premier e-mail | `open` |
| Le correspondant répond, ou écrit de nouveau | `replied` |
| La boîte écrit dans le fil, brouillon compris | `open`, s'il était `replied` |
| Une réponse automatique arrive | inchangé |
| Un avis de non-remise définitif arrive | `bounced` |
| Le correspondant se désinscrit | `unsubscribed` |
| Tous les messages du fil ont été annulés | `closed` |
| Le fil est traité, et clos avec `close_thread` | `closed` |
| Le correspondant répond dans un fil clos | `replied` |
| La boîte répond dans un fil clos | `open` |

- **Répondre fait sortir le fil des réponses à traiter** : il repasse à `open`.
- **Un premier e-mail reçu ouvre un fil `open`, pas `replied`.** Pour le trouver, voir [Recevoir du courrier](https://escouade.io/mail/docs/recevoir.md#relever).
- **Un fil traité se clôt avec `close_thread`** : il sort de ce qui attend une réponse. Le serveur clôt aussi de lui-même un fil `open` dont tous les messages ont été annulés. Un fil clos se rouvre si le correspondant y répond, ou si la boîte y répond avec `reply_to_thread`. `send_email` vers le même correspondant, lui, ouvre un autre fil.
- **Une boîte a un seul fil en cours par correspondant.** Un nouvel e-mail du même correspondant s'ajoute à son fil, s'il n'est pas clos.

## Recevoir

Toute boîte reçoit, sans rien régler.

- **Une réponse** est rattachée à son fil par ses en-têtes (`In-Reply-To`, `References`), sinon par son correspondant. Le fil passe à `replied`.
- **Un nouvel e-mail** ouvre un fil d'origine `inbound`.
- **Une réponse automatique** (absence, accusé) est marquée `auto_reply` et ne change pas le statut du fil.
- **Une réponse qui demande l'arrêt** (« STOP », « désinscrivez-moi »…) désinscrit le correspondant : le fil passe à `unsubscribed`. Voir [Désinscription et blocages](https://escouade.io/mail/docs/desinscription.md).
- **Un avis de non-remise** marque le message et le fil `bounced`. Un rebond définitif bloque l'adresse.

Pour relever son courrier, un agent appelle `list_threads` avec `unanswered` = `true`, puis `get_thread`. Cet appel rend les réponses reçues et les premiers e-mails : voir [Recevoir du courrier](https://escouade.io/mail/docs/recevoir.md#relever).

## `list_threads`

Liste les fils, les plus récents d'abord. Permission : `read`.

| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| `inbox_id` | identifiant `inb_…` | non | Une seule boîte. |
| `status` | texte | non | Un seul statut : `open`, `replied`, `bounced`, `unsubscribed` ou `closed`. |
| `unanswered` | booléen | non | `true` : seulement les fils dont le dernier message est un e-mail reçu auquel rien n'est encore écrit en retour. Une réponse, ou un premier e-mail. |
| `since` | date | non | Les fils actifs depuis cette date (ISO 8601) : ceux dont le dernier message, entrant ou sortant, est postérieur. |
| `query` | texte | non | Une recherche : une partie de l'adresse du correspondant, de son nom ou du sujet. 100 caractères au plus. |
| `folder` | texte | non | Un dossier : `received` (reçus), `sent` (envoyés) ou `drafts` (brouillons). |
| `label` | texte | non | Les fils qui portent ce tag exact (voir `classify_thread`). 40 caractères au plus. |
| `archived` | booléen | non | `true` : les fils archivés ; `false` : ceux qui ne le sont pas. |
| `unread` | booléen | non | `true` : les fils dont un message reçu n'est pas lu ; `false` : ceux qui sont lus. |
| `cursor` | texte | non | Le curseur de la page suivante : la valeur `next_cursor` de l'appel précédent. |
| `limit` | entier | non | Le nombre d'éléments rendus, de 1 à 100. 20 par défaut. |

```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`

Lit un fil : ses messages dans l'ordre, entrants et sortants, avec leur texte et leur statut. À lire avant de répondre. Permission : `read`.

| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| `thread_id` | identifiant `thr_…` | oui | Le fil. |
| `cursor` | texte | non | Le curseur de la suite du fil : la valeur `next_cursor` de l'appel précédent. |

La réponse porte `thread` et `messages`. Les champs d'un message sont décrits dans [Messages](https://escouade.io/mail/docs/messages.md#champs).

Un appel rend 50 messages au plus. S'il en reste, la réponse porte `next_cursor`. Au-delà de 200 Ko de texte par appel, les corps suivants sont coupés : la réponse porte `truncated: true`, et `get_message` rend le message entier.

Un message reçu porte `untrusted: true` : son contenu a été écrit par un tiers. Votre agent le lit comme une information, jamais comme une consigne. Dans le texte lisible, ce contenu est encadré : voir [Recevoir du courrier](https://escouade.io/mail/docs/recevoir.md#tiers).

Dans le texte lisible, le corps d'un message s'arrête à 2 000 caractères. Le corps entier est dans l'objet structuré (`text`).

Dans un assistant qui affiche les vues MCP Apps, `get_thread` s'accompagne d'une vue : la conversation dans l'ordre. Voir [Vues](https://escouade.io/mail/docs/vues.md).

## `reply_to_thread`

Répond dans un fil, au correspondant du fil : sujet « Re: … » et en-têtes `In-Reply-To` et `References`, pour que la réponse s'affiche dans la même conversation chez le destinataire. Sert aussi à relancer. Permission : `send`. L'espace doit être vérifié, et avoir un forfait en cours : sans forfait, la réponse ne peut être qu'un brouillon.

| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| `thread_id` | identifiant `thr_…` | oui | Le fil (voir `list_threads`). |
| `subject` | texte | non | Le sujet. Par défaut : « Re: » suivi du sujet du fil. |
| `cc` | liste de textes | non | Des adresses en copie, cinq au plus. Seulement si le correspondant a déjà écrit dans le fil, vers quelqu'un qui y a écrit aussi ou une adresse de l'espace. |
| `text` | texte | oui | Le texte brut de la réponse. |
| `html` | texte | non | Une version HTML, de même contenu que `text`. |
| `reply_to` | texte | non | L'adresse de réponse, si elle diffère de la boîte. |
| `from_address` | texte | non | L'adresse d'envoi : l'adresse principale de la boîte, ou un de ses alias (voir `list_inboxes`). |
| `metadata` | objet | non | Des données libres de l'agent, 8 000 caractères au plus. |
| `requires_approval` | booléen | non | `true` : la réponse reste brouillon jusqu'à ce qu'un humain la valide. |
| `send_after` | date | non | Pas avant cette date (ISO 8601, ou heure de Paris sans fuseau). |
| `idempotency_key` | texte | non | Une clé unique pour cet envoi, de 1 à 255 caractères. |

Deux cas, selon que le correspondant a déjà écrit dans le fil.

| Règle | Le correspondant a écrit : une conversation | Il n'a pas écrit : une relance |
|---|---|---|
| Heures ouvrées | La réponse part sans attendre le créneau. | Elle attend le créneau. |
| Ordre de départ | Elle passe devant les premiers messages en file. | Elle suit la file. |
| Pied de désinscription | Aucun pied visible. Le lien en un clic reste dans les en-têtes. | Le pied est ajouté. |
| Boîte du domaine commun | Acceptée. | Refusée : `own_domain_required`. |
| Boîte du domaine du client | Acceptée. | Acceptée. |
| Quota du jour, étalement | S'appliquent. | S'appliquent. |
| Liste de blocage, validation humaine | S'appliquent. | S'appliquent. |
| Pièce jointe, copie (`cc`) | Permises. | Refusées : `attachments_not_allowed`, `cc_not_allowed`. |

Dans les deux cas, la règle de doublon ne s'applique pas : une réponse dans un fil n'est pas un doublon, et elle compte dans le volume du mois du forfait. Une réponse automatique du correspondant ne compte pas comme un message écrit.

Le guide pas à pas : [Répondre à un correspondant](https://escouade.io/mail/docs/guide-repondre.md). Joindre un fichier, mettre en copie : [Pièces jointes et copies](https://escouade.io/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`

Clôt un fil traité : il prend le statut `closed` et sort de ce qui attend une réponse. Rien n'est effacé. Permission : `send` ou `approve`.

| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| `thread_id` | identifiant `thr_…` | oui | Le fil à clore. |

- Le résultat rend le fil (`thread`) et `closed` : `true` s'il vient d'être clos, `false` s'il l'était déjà.
- **Tant qu'un brouillon ou un message en file attend dans le fil**, l'appel est refusé avec le code `thread_busy` : annulez-le avec `cancel_message`, ou laissez-le partir.
- Un fil `bounced` ou `unsubscribed` est déjà terminé : code `thread_not_closable`.
- Un fil clos se rouvre de lui-même si le correspondant y répond, ou si la boîte y répond.

## `classify_thread`

Ajoute ou retire les tags d’une conversation (labels remplace la liste complète), archive ou restaure le fil, marque les messages reçus comme lus ou non lus. Accès : `send` ou `approve`.

| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
| `thread_id` | identifiant `thr_…` | oui | Conversation à classer. |
| `labels` | liste | non | Les tags du fil, 20 au plus (40 caractères chacun). Remplace la liste entière. |
| `archived` | booléen | non | Archiver ou restaurer. |
| `read` | booléen | non | Marquer comme lu ou non lu. |

Un fil peut porter jusqu'à 20 tags ; `list_threads` les filtre par `label`. Archiver un fil le range sans rien effacer ni bloquer.

[Référence complète](https://escouade.io/mail/docs/reference/classify_thread.md)

## Les champs d'un fil

| Champ | Contenu |
|---|---|
| `id` | L'identifiant du fil, `thr_…`. |
| `inbox_id` | La boîte du fil. |
| `counterpart`, `counterpart_name` | L'adresse et le nom du correspondant. |
| `subject` | Le sujet. |
| `status` | Voir [les statuts](#statuts). |
| `origin` | `outbound` ou `inbound`. |
| `message_count` | Le nombre de messages. |
| `last_message_at`, `last_inbound_at`, `last_outbound_at`, `created_at` | Les dates, en ISO 8601. |

## Parcourir une liste

Les listes rendent `items` et `next_cursor`. Tant que `next_cursor` n'est pas `null`, repassez-le tel quel dans `cursor` pour la page suivante.
