If your AI agent needs to sign up for services, handle customer support, or do anything that requires an inbox, this walks you through the options and tradeoffs. It makes a strong case for dedicated agent email infrastructure over handing your agent OAuth access to your Gmail (prompt injection risk, over-permissioned tokens). The comparison table is practical: AgentMail for two-way conversations, Resend or SendGrid for send-only notifications, SES if you're deep in AWS. Includes real code samples for support bots, sales outreach, and OTP extraction. The security section on prompt injection via email is worth reading even if you're just evaluating whether email belongs in your agent architecture at all.
npx -y skills add agentmail-to/agentmail-skills --skill email-for-ai-agents --agent claude-codeInstalls into .claude/skills of the current project.
Opinionated patterns for building AI agents that communicate over email. This skill covers architecture and security decisions, not SDK specifics. For AgentMail SDK usage, use the agentmail skill.
Giving an agent OAuth access to a human's Gmail account is the most common approach and the most dangerous:
gmail.modify) grant read/send/delete over the entire mailbox history, far beyond what any single task needsThe safer default: one dedicated, API-native inbox per agent (see Pattern 1).
Durable architectural constraints when choosing infrastructure (not a ranking):
| Provider | Key constraint |
|---|---|
| Gmail API | No programmatic inbox creation; no WebSocket push (Pub/Sub or polling only); access is revocable by Google at any time |
| Resend | No threads or conversation concept; cannot list/search received messages; inbound only via webhook, no persistent inbox |
| SendGrid | Inbound parse is stateless; no thread management; no programmatic inbox creation |
| Amazon SES | Inbound is rule-based (S3/Lambda triggers), not a mailbox; no thread management; no WebSocket support |
Every agent gets its own email address. Never share inboxes between agents.
client.inboxes.create(request=CreateInboxRequest(username="support-agent", client_id="support-v1"))
Why: clear sender identity, isolation (agents can't read each other's mail), per-agent auditability, and blast-radius containment if one agent is compromised.
Anti-pattern: one shared inbox with multiple agents reading from it. This creates race conditions and makes debugging impossible.
The core agent email pattern: agent sends, human replies, agent reads the reply and responds, looping until resolved.
Gotchas:
messages.list() returns metadata only (no body) -- call .get() on each item to fetch .text / .extracted_text.extracted_text / extracted_html for inbound replies so you don't reprocess the entire quoted chain on every turn.messages.reply(inbox_id, message_id, ...) with the parent message_id -- there is no thread_id parameter; AgentMail threads it automatically from the parent message.For high-stakes emails, let the agent draft and a human approve before sending: drafts.create(...) then drafts.send(inbox_id, draft_id).
Use drafts when:
Send directly when:
Default to event-driven delivery (WebSockets or webhooks) rather than polling. Polling is acceptable when neither is workable — e.g. a constrained environment with no public URL and no persistent connection — but expect higher latency and API usage.
| Factor | WebSockets | Webhooks |
|---|---|---|
| Public URL needed | No | Yes |
| Best for | Agents, bots, local dev | Servers, serverless |
| Latency | Lowest (persistent) | HTTP round-trip |
| Reconnection | You handle it | AgentMail retries |
Webhook payloads must be verified before use -- see references/threat-model.md.
For systems with multiple agents, assign clear roles (e.g. support@, sales@, billing@, router@) and use allow lists (references/threat-model.md) to restrict which external senders can reach each agent. For hub-and-spoke, peer-to-peer, and hierarchical escalation patterns, see references/topologies.md.
Agents that sign up for services need to receive and extract verification codes (e.g. regex for a 4-8 digit code in the inbound message text).
This applies to explicitly authorized first-party or test flows only -- e.g. your own agent signing up for a service it will operate, or a test account you control. It does not authorize automating sign-in, verification, or account-recovery flows for third-party accounts, or bypassing a service's terms of use or human-consent requirements.
Best practices:
Use labels to track message processing state within an inbox (add_labels / remove_labels on messages.update, then filter with messages.list(..., labels=[...])).
Common label schemes:
unread / processed / archivedneeds-reply / replied / escalatedbilling / support / sales (category routing)See references/threat-model.md for the full threat model. Critical rules:
references/threat-model.md.references/topologies.md -- hub-and-spoke, peer-to-peer, hierarchical, and multi-tenant pod agent email architecturesreferences/threat-model.md -- prompt injection, webhook spoofing, OAuth/credential exposure, data leakage, inbox enumeration, and the authorization matrixsickn33/antigravity-awesome-skills
moizibnyousaf/ai-agent-skills
github/awesome-copilot