Approval queue
The approval queue is the safety layer between your agent and the outside world. When your agent calls mail.send, the message doesn't go out immediately — it enters a queue where a human (or another agent) reviews and approves it.
This is the default behavior, and it's intentional. Agents make mistakes. They hallucinate. They misinterpret context. The approval queue ensures a human is always in the loop for outbound communication, until you've built enough confidence to relax the rules.
Approval modes
Each mailbox has an approval_mode that controls how outbound messages are handled:
all — Review everything
Every outbound message is held. Nothing sends without explicit human approval. This is the default and the recommended starting point.
smart — Auto-approve trusted patterns
Messages matching your auto-approve rules send immediately. Everything else is held. This is the mode you graduate to after building confidence.
none — No review
Messages send immediately. No queue. Use this only for fully automated workflows where you've validated the agent's behavior extensively and accept the risk.
Auto-approve rules
When approval_mode is smart, you define rules that determine which messages skip the queue. Rules are evaluated in order — the first match wins.
mailbox.update({
id: "mbx_ops",
approval_mode: "smart",
auto_approve_rules: [
// Auto-approve replies to existing threads
{ type: "reply" },
// Auto-approve messages to specific domains
{ type: "domain", match: "acme.com" },
{ type: "domain", match: "*.internal.co" },
// Auto-approve messages to contacts in a named list
{ type: "contact_list", list: "approved_vendors" },
// Auto-approve based on subject pattern
{ type: "subject", match: "RE:*" },
// Auto-approve all messages (makes smart behave like none)
{ type: "all" }
]
})
Rule types
- reply — Matches messages that are replies to existing inbound threads. The agent is responding to someone who emailed first.
- domain — Matches messages where all recipients are at a specified domain. Supports glob patterns (
*.acme.com). - contact_list — Matches messages where all recipients are in a named contact list. Manage lists with
contact.create. - subject — Matches messages whose subject matches a pattern. Glob patterns supported.
- all — Matches everything. Effectively disables the queue for this mailbox. Place last.
Reviewing the queue
There are three ways to review pending messages:
1. Via MCP tools (agent-assisted review)
A human asks their agent "show me pending emails" and the agent calls:
const pending = await queue.list({
status: "pending"
})
// Returns array of queued messages with full preview
// { id, to, subject, body, attachments, queued_at, mailbox }
The human reviews and the agent approves:
await queue.approve({ message_id: "msg_..." })
// Or approve with edits
await queue.approve({
message_id: "msg_...",
edits: {
body: "Revised message text..."
}
})
// Or reject
await queue.reject({
message_id: "msg_...",
reason: "Too aggressive tone"
})
2. Via the dashboard
The MCPMail dashboard shows all pending messages with one-click approve/reject. Useful for reviewing a batch quickly.
3. Via webhook + external tool
Register a webhook for queue.pending events. When a message enters the queue, MCPMail posts a notification to your URL. You can build approval flows in Slack, Teams, or any internal tool.
webhook.register({
url: "https://your-app.com/webhooks/mcpmail",
events: ["queue.pending"]
})
Queue behavior with mail.wait
When your agent calls mail.send followed by mail.wait, the wait begins after the message is approved and sent, not when it enters the queue. If the message is rejected, mail.wait returns a rejection result instead of waiting for a reply that will never come.
// mail.send puts the message in the queue
const sent = await mail.send({ ... })
// sent.status is "queued" — not sent yet
// mail.wait handles the queue transparently
const reply = await mail.wait({
after_message: sent.id,
timeout: "48h"
})
// If the queued message was rejected:
// reply = { rejected: true, reason: "..." }
// If approved, sent, and a reply arrived:
// reply = { body: "...", from: "...", ... }
Graduation path
The recommended path from full review to autonomous sending:
- Start with
all— Review every message. Build confidence in what the agent produces. - Move to
smartwith conservative rules — Auto-approve replies to threads. Hold everything else. - Expand rules gradually — Add trusted domains, contact lists, subject patterns as you verify the agent handles them correctly.
- Consider
noneonly for low-risk workflows — Internal notifications, status updates, automated reports where a bad message is annoying but not damaging.