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

# Approve an agent's sends

> The agent writes, a human reviews, nothing leaves before they approve. Two accesses, two roles: the agent's key writes drafts, the human's assistant approves them. The server checks the permission on every approval.

The tool responses below are examples, in the server's format; the server answers in French. Names, addresses and identifiers are fictitious.

## Who does what

| Role | Their access | What they do | Tools |
|---|---|---|---|
| The agent | A `sak_…` key, at `https://api.subagent.fr/mcp`. It carries `send`, `read`, `manage_inboxes`. | It writes drafts, corrects them, cancels them. | `send_email`, `reply_to_thread`, `update_draft`, `cancel_message` |
| The human | Their assistant, connected at `https://mail.escouade.io/mcp`. It carries what the human authorised, `approve` included. | They review, approve, correct or refuse, from their assistant. | `list_drafts`, `approve_message`, `update_draft`, `cancel_message` |

The key returned by `sign_up` does not carry `approve`. The human has no key to ask for: they sign in with their e-mail. See [Connector](https://escouade.io/en/mail/docs/connecteur.md).

## Before you start

- **The space is verified**, and the human's e-mail is attached to it: it is the one they sign in with.
- **The space has an active plan.** Without a plan, a draft can be prepared but not approved: refusal `payment_required`. See [Pricing and payment](https://escouade.io/en/pricing).
- **For a first message, the inbox is on the human's domain.** From the inbox on the shared domain, only a reply to a correspondent who has written can be approved.

## 1. The agent leaves a draft

The agent calls `send_email` or `reply_to_thread` with `requires_approval` = `true`. The message takes the `draft` status. The response says where the human approves.

```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://mail.escouade.io/mcp, 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.
```

Some spaces require approval for every send. `inbox_status` says so: `require_approval` is `true`. The agent then has nothing to pass.

A draft only counts towards its inbox's daily quota at the moment it leaves.

## 2. The agent tells its human

The server sends the human no notice when a draft is waiting. The agent tells them, in its conversation: how many drafts, for whom, and the address to connect their assistant to, `https://mail.escouade.io/mcp`.

`inbox_status` returns each inbox's number of drafts to approve (`drafts`).

## 3. The human connects their assistant

Only once. In their assistant, the human adds a connector at `https://mail.escouade.io/mcp`. An Escouade Mail page opens: they sign in with their Escouade account, at the address attached to their space, then give their consent. Assistant by assistant: [Claude](https://escouade.io/en/mail/docs/brancher/claude.md), [ChatGPT](https://escouade.io/en/mail/docs/brancher/chatgpt.md). The details: [Connector](https://escouade.io/en/mail/docs/connecteur.md#brancher).

## 4. The human reviews

In their assistant, the human asks: “Show me the drafts.” The assistant calls `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` returns everything that will leave: the full text, the reply-to address if it differs from the inbox, and the HTML version if there is one. The text points them out; the structured object carries them (`reply_to`, `has_html`, `html`).

In an assistant that displays MCP Apps views, `list_drafts` comes with a view: the queue of drafts, with “Valider” (approve) and “Refuser” (refuse). See [Views](https://escouade.io/en/mail/docs/vues.md#valider).

## 5. The human decides

| Decision | What the human says | Tool | Effect |
|---|---|---|---|
| Approve | “Approve the first one.” | `approve_message` | The draft moves to the queue. It leaves within working hours, according to the inbox's quota. |
| Correct | “Replace Friday with Monday.” | `update_draft` | The subject, the text or the HTML change. The draft still waits. |
| Refuse | “Cancel it.” | `cancel_message` | The draft will not leave. |

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

- **The name of the access that approved** is written in the message (`approved_by`) and in its log: the application, followed by the human's e-mail.
- **Approval applies to what was read.** With `revision`, the value returned by `list_drafts`, approval is refused if the draft has changed since: code `draft_changed`. Read it again, then approve the version read.
- **A draft's recipient and inbox do not change.** For that, cancel the draft and write a new message.

## 6. The agent follows up

After approval, the message has the `queued` status, then `sent`. The agent follows it with `get_message`: the log says who approved, and when the message left. For a queued message, the text says why it waits and the earliest time it can leave.

| What the agent sees | Meaning |
|---|---|
| `status` = `draft` | The draft still waits. |
| `status` = `queued`, `approved_by` filled in | Approved: it will leave. |
| `status` = `cancelled` | Refused by the human, or cancelled. `status_code` is `cancelled_by_key`. |
| The text has changed | The human corrected it before approving. The log carries the correction and its author. |

## If the agent tries to approve

The server refuses. The permission is checked on every call, and the agent's key does not carry it.

```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://mail.escouade.io/mcp où il se connecte avec son e-mail.
```

Over MCP, `approve_message` does not even appear in the agent's tool list.

## Approval refusals

| Code | What happened | What to do |
|---|---|---|
| `forbidden` | The access does not carry `approve`. | Approval is for the human, from their assistant connected at `https://mail.escouade.io/mcp`. |
| `payment_required` | The space has no active plan. | The agent calls `subscribe` and hands the link to its human. |
| `own_domain_required` | The draft leaves from the inbox on the shared domain, and it is not a reply to a correspondent who has written. | Write it from an inbox on the human's domain. |
| `draft_changed` | The draft was changed after it was read. | Read it again with `list_drafts`, then approve the version read. |
| `not_draft` | The message is no longer a draft: it is already approved, sent or cancelled. | Read its status again with `get_message`. |
| `recipient_suppressed` | The recipient was blocked after the draft was created. | Stop writing to that address. |

The details of each tool: [Drafts and approval](https://escouade.io/en/mail/docs/brouillons.md).
