For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
contact@agentmail.ccDiscord
DocumentationAPI ReferenceKnowledge BaseChangelog
DocumentationAPI ReferenceKnowledge BaseChangelog
    • Introduction
  • Getting Started
    • What is AgentMail?
    • What can I do with an inbox?
    • Creating your first inbox
    • Getting your API key
  • Agent Patterns
    • Handling inbound emails
    • Allowlists & blocklists
    • Managing threaded conversations
    • Human-in-the-loop workflows
    • Pods for multi-tenant email
    • Using labels to track state
  • Domains & Deliverability
    • Custom domain setup
    • SPF, DKIM, and DMARC setup
    • Emails going to spam
    • Warming Up
    • MX record conflicts
  • Troubleshooting
    • API 403 error
    • Rate limits
    • Preventing duplicate sends
    • Domain not verifying
    • Emails bouncing
    • Why are my emails not showing up?
  • DNS Guides
    • Cloudflare
    • GoDaddy
    • Route 53 (AWS)
    • Namecheap
LogoLogo
contact@agentmail.ccDiscord
On this page
  • 1. CC or BCC a human on every email
  • 2. Drafts for review before sending
  • 3. Labels for escalation
  • 4. Allowlists as guardrails
  • Best practices
Agent Patterns

How do I build a human-in-the-loop workflow?

Keep humans in control of your agent's email communications.
Was this page helpful?
Edit this page
Previous

How do I use Pods for multi-tenant email?

Isolate inboxes, domains, and data across tenants with Pods.
Next
Built with

AgentMail provides several mechanisms for keeping humans involved when agents send emails. You can combine these approaches to match the level of oversight your workflow requires.

1. CC or BCC a human on every email

The simplest approach: copy a human on every email your agent sends so they have full visibility.

Python
1client.inboxes.messages.send(
2 inbox_id="agent@yourdomain.com",
3 to=["customer@example.com"],
4 cc=["manager@yourcompany.com"],
5 subject="Re: Your request",
6 text="I've processed your refund...",
7 html="<p>I've processed your refund...</p>"
8)

The manager sees every outgoing email and can step in if something looks wrong. Use bcc instead of cc if you want the oversight to be invisible to the recipient.

2. Drafts for review before sending

Use Drafts to let your agent compose emails that a human reviews and approves before they go out. This is ideal for high-stakes emails like contracts, legal communications, or financial matters.

Python
1# Agent composes a draft
2draft = client.inboxes.drafts.create(
3 inbox_id="agent@yourdomain.com",
4 to=["important-client@example.com"],
5 subject="Contract proposal",
6 text="Here is our proposal...",
7 html="<p>Here is our proposal...</p>"
8)
9
10print(f"Draft created: {draft.draft_id}")
11
12# Human reviews the draft, then approves it
13sent_message = client.inboxes.drafts.send(
14 inbox_id="agent@yourdomain.com",
15 draft_id=draft.draft_id
16)

You can also list all pending drafts across your entire organization, which is useful for building a central approval dashboard:

Python
1# List all drafts across every inbox in the organization
2all_drafts = client.drafts.list()
3print(f"{all_drafts.count} drafts pending review")

For more details, see the Drafts core concept documentation.

3. Labels for escalation

Use labels to flag messages that need human attention. Your agent can detect situations it cannot handle and tag them for review.

Python
1# Agent detects a situation that needs human review
2client.inboxes.messages.update(
3 inbox_id="agent@yourdomain.com",
4 message_id=msg.message_id,
5 add_labels=["needs-human-review", "escalation"]
6)

Then build a dashboard or scheduled job that queries for flagged messages:

Python
1# Periodically check for messages that need human review
2flagged = client.inboxes.messages.list(
3 inbox_id="agent@yourdomain.com",
4 labels=["needs-human-review"]
5)
6
7for msg in flagged.messages:
8 notify_human(msg)

4. Allowlists as guardrails

Use a send allowlist to restrict which addresses your agent can email. If the agent tries to send to an address or domain not on the list, AgentMail will reject the request and the email will not go out.

Python
1# Only allow sending to known customer domains
2client.lists.create("send", "allow", entry="trusted-customer.com")
3client.lists.create("send", "allow", entry="partner-corp.com")

This acts as a hard safety boundary. Your agent can only email recipients you have explicitly approved, regardless of what the agent logic tries to do.

Best practices

  • Start with CC + drafts for new agents until you trust their behavior
  • Graduate to autonomous sending for routine emails, and keep drafts for high-value communications
  • Always have an escalation path: agents should know when to stop and ask for help
  • Use labels consistently across your agents so dashboards and alerting work reliably
  • Combine approaches: for example, use allowlists for autonomous sending to known recipients, drafts for unknown recipients, and CC a human on everything during the first week