Drafts and approval
A send can wait for human approval. The message stays a draft, a human reviews it in their assistant, then approves, corrects or cancels it. An agent does not approve its own sends.
When a message stays a draft
- The message asks for it:
send_emailorreply_to_threadwithrequires_approval=true. - The space requires it: every send stays a draft.
inbox_statussays so (require_approval). The space’s human sets it from their assistant, withupdate_settings.
A draft has the status draft. Files are attached to a draft: see Attachments and copies. It only counts towards its inbox’s daily quota at the moment it leaves. It can be prepared even before payment: it can only be approved with an active plan.
The response tells the agent where its 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.Who approves
Approval needs the approve permission. The server checks it on every call to approve_message.
- The key returned by
sign_upcarriessend,readandmanage_inboxes. It does not carryapprove: the agent that signed up cannot approve its own sends. - The human gets
approvethrough the connector. They connect their assistant (Claude, ChatGPT…) athttps://mail.escouade.io/mcp, sign in with their Escouade account, at the address attached to their space, then authorise. There is no key to copy. See Connector. - Approval happens in the assistant: through an explicit request from the human (“approve the first one”), which becomes a call to
approve_message, or through the “Valider” (approve) gesture in the drafts view. - The server does not tell the human that a draft is waiting: the agent tells them.
The full path, on the agent’s side and the human’s side: Approve an agent’s sends.
Without the permission, the call is refused:
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.
list_drafts
Lists the drafts waiting for approval, with their full text, newest first. Permission: read or approve.
| Parameter | Type | Required | Description |
|---|---|---|---|
inbox_id | inb_… identifier | no | A single inbox. |
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. |
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
The structured object carries items, next_cursor and key: the key that reads, with its name and permissions. Each draft carries its version (revision), to pass back to approve_message.
update_draft
Changes a draft’s subject, text or HTML, as long as it is not approved. The recipient and the inbox do not change: for that, cancel the draft and write a new message. Every correction is noted in the log, with its author. Permission: send or approve.
| Parameter | Type | Required | Description |
|---|---|---|---|
message_id | msg_… identifier | yes | The draft (see list_drafts). |
subject | text | no | The new subject. |
text | text | no | The new text. |
html | text | no | The new HTML. An empty text removes the HTML version. |
approve_message
Approves a draft: it moves to the queue and will leave within the sending window, according to the quotas. Permission: approve.
| Parameter | Type | Required | Description |
|---|---|---|---|
message_id | msg_… identifier | yes | The draft (see list_drafts). |
revision | text | no | The version of the draft as it was read: the revision field returned by list_drafts. |
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’s name, followed by the human’s e-mail. - Without an active plan, approval is refused with the code
payment_required. A draft of a test send can always be approved. - A draft written from the inbox on the shared domain can only be approved if it replies to a correspondent who wrote: otherwise, refusal
own_domain_required. - If the recipient has been blocked since the draft was created, approval is refused with the code
recipient_suppressed. - A message that is no longer a draft is refused with the code
not_draft. - With
revision, approval only applies to what was read: if the draft has changed since, it is refused with the codedraft_changed. The drafts view passes it on by itself. - Before approving, the human sees everything that will leave:
list_draftsalso returns the reply-to address (reply_to) and the HTML version (has_html,html), and points them out in its text.
Cancelling a draft
cancel_message cancels a draft or a queued message. See Messages.
In the assistant
In an assistant that displays MCP Apps views, list_drafts comes with a view: the queue of drafts, with the gestures “Valider” (approve) and “Refuser” (refuse). “Valider” calls approve_message with the version displayed; “Refuser” calls cancel_message. Without the approve permission, “Valider” is absent.
Correcting a draft is asked for in the conversation: the assistant calls update_draft. Without a view, everything is read and approved through the text. See Views.