Review an AgentMail threaded draft before sending
Create an unsent reply draft from a source message, bind human approval to its exact revision, and separate draft creation from send permission.
Reviewed
AgentMail drafts let an agent prepare a message without immediately sending it. Keep the agent’s key unable to send, and let a separate approved-send service hold that capability. A draft labeled “reviewed” is not an enforced authorization boundary by itself.
Create an unsent reply with the official SDK
Install agentmail. Use a runtime key with the necessary source-message read and draft-create permissions for the intended inbox. This worker excerpt creates a draft, not a live send; source and proposedText must come from your validated application job:
import { AgentMailClient } from "agentmail";
if (!process.env.AGENTMAIL_DRAFT_KEY) throw new Error("Missing draft credential");
const client = new AgentMailClient({ apiKey: process.env.AGENTMAIL_DRAFT_KEY });
const draft = await client.inboxes.drafts.create(source.inboxId, {
inReplyTo: source.messageId,
text: proposedText,
});
// Persist draft.draftId and the exact reviewable proposal in your application.
The drafts reference documents deriving subject/threading and reply recipients from inReplyTo. Inspect the resulting recipients before approval. Reply-all expands exposure; do not enable it implicitly. Never treat a message’s claimed identity as permission to include private attachments or account data.
Enforce review outside the model
- Retrieve the stored draft and show the actual inbox, recipients, subject, body, and attachments to an authorized reviewer.
- Bind approval to the exact content/revision; prevent concurrent edits through your application while the approved send is claimed.
- Keep
draft_sendand directmessage_sendout of the drafting agent’s whitelist. Grant send authority only to the trusted executor. - Do not set
sendAton an unapproved draft: scheduled delivery is a send action, not a harmless reminder to review later.
After approval, the trusted executor uses the documented inboxes.drafts.send(inboxId, draftId) operation. AgentMail documents that sending converts the draft to a message and deletes the draft; retain the resulting message/thread identity in your application.
Recover without duplicate replies
Use AgentMail’s documented send idempotency, including the supported SDK request options, for retries of the same approved operation. Resource clientId and send Idempotency-Key are different mechanisms; do not assume one covers the other or that provider deduplication lasts indefinitely.
If the draft changed, approval is stale. If a send is uncertain, reconcile message state before replaying. Test revision changes, denied agent sends, duplicate executor jobs, and reviewer rejection without contacting real recipients. Permissions are the enforcement layer; instructions asking the model to “please wait” are not enough.