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.
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. - 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.
Brouillon msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 dans le fil thr_0e5a7c2b9f1d48e6a3c7b5d0e2f9a814 : de [email protected] à [email protected], « 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, ChatGPT. The details: Connector.
4. The human reviews
In their assistant, the human asks: “Show me the drafts.” The assistant calls list_drafts.
1 brouillon(s) à valider : [msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 · sortant · en brouillon (attend une validation) · mardi 13 octobre à 14 h 02] De : Atelier Exemple <[email protected]> À : [email protected] 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.
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. |
Validé par Claude ([email protected]) : msg_7f2a91c4e8b34d0fa6c25e1b9d3f7a48 ([email protected] → [email protected], « 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 bylist_drafts, approval is refused if the draft has changed since: codedraft_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.
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.