> Ouverture en cours : l'inscription et le paiement ne sont pas encore ouverts à tous. L'état du jour : https://subagent.fr/docs/etat.md
>
> Pour un agent : la carte du site est sur https://subagent.fr/llms.txt ; chaque page existe en Markdown, à la même adresse suffixée `.md`.

# Valider les envois d'un agent

> L'agent écrit, un humain relit, rien ne part avant sa validation. Deux accès, deux rôles : la clé de l'agent écrit des brouillons, l'assistant de l'humain les valide. Le serveur vérifie la permission à chaque validation.

Les réponses d'outils ci-dessous sont des exemples, au format du serveur. Les noms, adresses et identifiants sont fictifs.

## Qui fait quoi

| Rôle | Son accès | Ce qu'il fait | Outils |
|---|---|---|---|
| L'agent | Une clé `sak_…`, à l'adresse `https://api.subagent.fr/mcp`. Elle porte `send`, `read`, `manage_inboxes`. | Il écrit des brouillons, les corrige, les annule. | `send_email`, `reply_to_thread`, `update_draft`, `cancel_message` |
| L'humain | Son assistant, branché à l'adresse `https://api.subagent.fr/connect`. Il porte ce que l'humain a autorisé, dont `approve`. | Il relit, valide, corrige ou refuse, depuis son assistant. | `list_drafts`, `approve_message`, `update_draft`, `cancel_message` |

La clé rendue par `sign_up` ne porte pas `approve`. L'humain n'a pas de clé à demander : il se connecte avec son e-mail. Voir [Connecteur](https://subagent.fr/docs/connecteur.md).

## Avant de commencer

- **L'espace est vérifié**, et l'e-mail de l'humain y est rattaché : c'est avec lui qu'il se connecte.
- **L'espace a un forfait en cours.** Sans forfait, un brouillon se prépare mais ne se valide pas : refus `payment_required`. Voir [Prix et paiement](https://subagent.fr/docs/prix.md).
- **Pour un premier message, la boîte est sur le domaine de l'humain.** Depuis la boîte du domaine commun, seule une réponse à un correspondant qui a écrit se valide.

## 1. L'agent laisse un brouillon

L'agent appelle `send_email` ou `reply_to_thread` avec `requires_approval` = `true`. Le message prend le statut `draft`. La réponse dit où l'humain valide.

```text
Brouillon msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 dans le fil thr_0e5a7c2b9f1d48e6a3c7b5d0e2f9a814 : de agent@exemple.fr à nadia.morel@exemple.com, « Rendez-vous de jeudi ». Il attend une validation humaine : votre humain (m•••@exemple.fr) branche son assistant (Claude, ChatGPT…) à l’adresse https://api.subagent.fr/connect, s’y connecte avec cet e-mail, puis relit le brouillon et le valide ou le corrige. Dites-le-lui : aucun avis ne lui est envoyé. Rien ne part avant.
```

Certains espaces imposent la validation pour tout envoi. `inbox_status` le dit : `require_approval` vaut `true`. L'agent n'a alors rien à passer.

Un brouillon ne compte dans le quota du jour de sa boîte qu'au moment de partir.

## 2. L'agent prévient son humain

Le serveur n'envoie aucun avis à l'humain quand un brouillon attend. L'agent le lui dit, dans sa conversation : combien de brouillons, pour qui, et l'adresse où brancher son assistant, `https://api.subagent.fr/connect`.

`inbox_status` rend le nombre de brouillons à valider de chaque boîte (`drafts`).

## 3. L'humain branche son assistant

Une seule fois. Dans son assistant, l'humain ajoute un connecteur à l'adresse `https://api.subagent.fr/connect`. Une page de Subagent lui demande l'e-mail rattaché à son espace, puis le code à 6 chiffres qu'il vient de recevoir, puis son accord. Assistant par assistant : [Claude](https://subagent.fr/brancher/claude.md), [ChatGPT](https://subagent.fr/brancher/chatgpt.md). Le détail : [Connecteur](https://subagent.fr/docs/connecteur.md#brancher).

## 4. L'humain relit

Dans son assistant, l'humain demande : « Montre-moi les brouillons. » L'assistant appelle `list_drafts`.

```text
1 brouillon(s) à valider :

[msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 · sortant · en brouillon (attend une validation) · mardi 13 octobre à 14 h 02]
De : Atelier Exemple <agent@exemple.fr>
À : nadia.morel@exemple.com
Sujet : Rendez-vous de jeudi

Bonjour Madame Morel,

Pouvons-nous déplacer notre rendez-vous de jeudi à vendredi, à la même heure ?

Atelier Exemple
```

`list_drafts` rend tout ce qui partira : le texte complet, l'adresse de réponse si elle diffère de la boîte, et la version HTML s'il y en a une. Le texte les signale ; l'objet structuré les porte (`reply_to`, `has_html`, `html`).

Dans un assistant qui affiche les vues MCP Apps, `list_drafts` s'accompagne d'une vue : la file des brouillons, avec « Valider » et « Refuser ». Voir [Vues](https://subagent.fr/docs/vues.md#valider).

## 5. L'humain décide

| Décision | Ce que dit l'humain | Outil | Effet |
|---|---|---|---|
| Valider | « Valide le premier. » | `approve_message` | Le brouillon passe en file. Il part dans les heures ouvrées, selon le quota de la boîte. |
| Corriger | « Remplace vendredi par lundi. » | `update_draft` | Le sujet, le texte ou le HTML changent. Le brouillon attend toujours. |
| Refuser | « Annule-le. » | `cancel_message` | Le brouillon ne partira pas. |

```text
Validé par Claude (marie@exemple.fr) : msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 (agent@exemple.fr → nadia.morel@exemple.com, « Rendez-vous de jeudi ») est en file. Il partira dans le créneau, selon le quota de la boîte (inbox_status).
```

- **Le nom de l'accès qui a validé** est écrit dans le message (`approved_by`) et dans son journal : l'application, suivie de l'e-mail de l'humain.
- **La validation vaut pour ce qui a été lu.** Avec `revision`, la valeur rendue par `list_drafts`, la validation est refusée si le brouillon a changé depuis : code `draft_changed`. Il faut le relire, puis valider la version lue.
- **Le destinataire et la boîte d'un brouillon ne changent pas.** Pour cela, on annule le brouillon et on écrit un nouveau message.

## 6. L'agent suit

Après la validation, le message a le statut `queued`, puis `sent`. L'agent le suit avec `get_message` : le journal dit qui a validé, et quand le message est parti. Pour un message en file, le texte dit pourquoi il attend et quand il peut partir au plus tôt.

| Ce que voit l'agent | Sens |
|---|---|
| `status` = `draft` | Le brouillon attend toujours. |
| `status` = `queued`, `approved_by` renseigné | Validé : il partira. |
| `status` = `cancelled` | Refusé par l'humain, ou annulé. `status_code` vaut `cancelled_by_key`. |
| Le texte a changé | L'humain l'a corrigé avant de valider. Le journal porte la correction et son auteur. |

## Si l'agent essaie de valider

Le serveur refuse. La permission est vérifiée à chaque appel, et la clé de l'agent ne la porte pas.

```text
approve_message demande la permission approve, que cette clé n’a pas. (code forbidden)
→ La validation est un geste humain : votre humain l’accomplit depuis son assistant (Claude, ChatGPT…), branché à l’adresse https://api.subagent.fr/connect où il se connecte avec son e-mail.
```

Par MCP, `approve_message` n'apparaît même pas dans la liste d'outils de l'agent.

## Les refus de la validation

| Code | Ce qui s'est passé | Que faire |
|---|---|---|
| `forbidden` | L'accès ne porte pas `approve`. | La validation revient à l'humain, depuis son assistant branché à `https://api.subagent.fr/connect`. |
| `payment_required` | L'espace n'a pas de forfait en cours. | L'agent appelle `subscribe` et remet le lien à son humain. |
| `own_domain_required` | Le brouillon part de la boîte du domaine commun, et ce n'est pas une réponse à un correspondant qui a écrit. | L'écrire depuis une boîte du domaine de l'humain. |
| `draft_changed` | Le brouillon a été modifié depuis sa lecture. | Relire avec `list_drafts`, puis valider la version lue. |
| `not_draft` | Le message n'est plus un brouillon : il est déjà validé, parti ou annulé. | Relire son statut avec `get_message`. |
| `recipient_suppressed` | Le destinataire a été bloqué depuis la création du brouillon. | Ne plus écrire à cette adresse. |

Le détail de chaque outil : [Brouillons et validation](https://subagent.fr/docs/brouillons.md).
