TL;DR: Giving each AI agent a dedicated email address means clean inbox isolation, per-agent deliverability tracking, and unambiguous reply routing. The core pattern uses subdomain or plus-address schemes tied to webhook endpoints, with SPF/DKIM scoped per sending identity. This post walks through the full setup.

Email for AI agents isn't just about sending notifications — it's about giving each agent a real identity on the internet, one that can send, receive, thread conversations, and route replies to the right logic without collisions. When multiple agents share a single address, you get reply ambiguity, interleaved threads, and no way to scope deliverability metrics. The fix is simple: one address (or at minimum one logical namespace) per agent.
This post covers addressing schemes, the DNS and authentication work required, inbox isolation mechanics, and how inbound routing ties it all together.
Why per-agent addresses matter
One address per agent eliminates the reply routing problem at the source. When agent-billing@yourco.ai receives a reply, you know exactly which agent should process it — no header inspection, no thread-ID lookup, no guessing. Contrast that with a shared agents@yourco.ai inbox where every inbound message needs a dispatch layer to figure out who owns it.
Per-agent addresses also let you:
- Track deliverability per agent type. An agent that sends invoices has different engagement patterns than one sending onboarding sequences. Mixing them into one sender identity muddies your reputation data.
- Rotate or retire agents cleanly. Decommissioning
agent-onboarding-v1@yourco.aidoesn't touch any other agent's reputation or thread history. - Enforce access control. Agent A cannot accidentally read Agent B's inbox if they're logically or physically separated.
- Meet compliance boundaries. Some workflows — support, billing, legal — need auditable, isolated mailstreams.
Addressing schemes: three patterns
There are three practical patterns, each with different operational tradeoffs.
1. Subdomain per agent (or agent class)
billing-agent@billing.agents.yourco.ai
onboarding-agent@onboarding.agents.yourco.ai
support-agent@support.agents.yourco.ai
Each subdomain gets its own MX records, SPF record, and DKIM key. This is the cleanest isolation: DNS-level separation means you can tune deliverability independently, and a reputation issue with one subdomain doesn't bleed into others.
DNS setup for a subdomain:
; MX — point to your inbound processor
billing.agents.yourco.ai. MX 10 inbound.mails.ai.
; SPF — authorize only your sending infrastructure
billing.agents.yourco.ai. TXT "v=spf1 include:spf.mails.ai ~all"
; DKIM — one key per subdomain/selector
billing._domainkey.billing.agents.yourco.ai. TXT "v=DKIM1; k=rsa; p=..."
; DMARC — per-subdomain policy
_dmarc.billing.agents.yourco.ai. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourco.ai"
The downside: DNS overhead scales with the number of agent classes. For 5–10 distinct agents this is fine. For hundreds of ephemeral agents, you need a different approach.
2. Plus-address (sub-address) routing
agents+billing-001@yourco.ai
agents+onboarding-007@yourco.ai
agents+support-abc123@yourco.ai
The +tag portion is preserved in the To: header and available in the raw message, but delivery goes to the same mailbox (or catch-all). Your inbound processor extracts the tag and routes accordingly.
This works well for short-lived or dynamically-created agents where you don't want to provision DNS per agent. The tradeoff: all addresses share the same sending domain's reputation, and replies go to the same catch-all — you're back to needing a dispatch layer, just a lighter one since the tag is explicit in the address.
Extracting the tag in a webhook handler (TypeScript):
function extractAgentId(toAddress: string): string | null {
// "agents+billing-001@yourco.ai" → "billing-001"
const match = toAddress.match(/^[^+]+\+([^@]+)@/);
return match ? match[1] : null;
}
// In your webhook handler:
app.post('/inbound', (req, res) => {
const { to, subject, text, html, messageId } = req.body;
const agentId = extractAgentId(to);
if (!agentId) return res.status(400).send('No agent tag');
dispatchToAgent(agentId, { subject, text, html, messageId });
res.status(200).send('ok');
});
3. Dynamic address generation (per-thread)
For conversational agents, you generate a unique reply-to address per outbound message:
re+a8f2c91d@reply.yourco.ai ← ties to thread/agent/context
re+b3e7a042@reply.yourco.ai
The token a8f2c91d maps to a record in your database: { agentId, threadId, contextSnapshot, expiresAt }. When a reply arrives, you decode the token, hydrate the agent's context, and continue the conversation.
This is the most powerful pattern for conversational agents because it eliminates thread ambiguity entirely — each reply carries its own routing key. The implementation cost is a token store (Redis or Postgres works fine) and a catch-all MX on your reply subdomain.
sequenceDiagram
participant Agent
participant TokenStore
participant EmailInfra
participant User
participant Webhook
Agent->>TokenStore: store context token a8f2c91d
Agent->>EmailInfra: send From billing-agent reply-to re+a8f2c91d at reply domain
EmailInfra->>User: email delivered
User->>EmailInfra: reply to re+a8f2c91d
EmailInfra->>Webhook: POST inbound event
Webhook->>TokenStore: lookup a8f2c91d
TokenStore->>Webhook: agentId plus threadId plus context
Webhook->>Agent: resume with context
DNS authentication: what you actually need to configure
Every agent address that sends email needs SPF, DKIM, and ideally DMARC. These aren't optional — without them, your agent's email lands in spam or gets rejected outright. According to Google's Sender Guidelines (updated 2024), bulk senders to Gmail must have DKIM signing and a DMARC policy or risk delivery failures.
SPF authorizes which IP ranges can send as your domain. For agents using a managed sending API, this is usually an include: directive pointing to your provider's SPF record.
DKIM signs outbound messages with a private key. The public key sits in DNS. Receivers verify the signature to confirm the message wasn't tampered with and came from an authorized signer. Use 2048-bit keys minimum; 4096-bit is better for high-value agent identities.
DMARC tells receivers what to do with messages that fail SPF/DKIM alignment. Start with p=none for monitoring, move to p=quarantine once you've confirmed your agent sends are fully authenticated, then p=reject for maximum protection.
For per-agent subdomains, set a subdomain-specific DMARC record. If you don't, receivers fall back to the organizational domain's DMARC policy — which may be more permissive or more restrictive than you want for that agent.
Inbox isolation: physical vs. logical
Isolation has two flavors.
Logical isolation uses a single mailbox or catch-all with routing rules. Messages for different agents land in the same storage system but get tagged and dispatched to different queues or handlers. Operationally simpler — but a bug in your routing layer could expose one agent's messages to another agent's handler.
Physical isolation gives each agent its own mailbox, IMAP account, or inbound webhook endpoint. No shared catch-all. Messages for billing-agent@... go to a completely separate store from messages for onboarding-agent@.... More infrastructure overhead, but clean security boundaries and easier per-agent access control.
For most production multi-agent systems: use physical isolation for agents that handle sensitive data (billing, legal, auth flows), and logical isolation with tight dispatch logic for agents with lower data sensitivity.
Inbound parsing and webhook routing
Once DNS is configured and your MX records point to an inbound processor, you need to parse and route incoming messages. The inbound pipeline looks like this:
- Message arrives at your MX host
- Host delivers to a webhook endpoint (HTTP POST with parsed message fields)
- Your handler extracts
To:,From:,Message-ID:,In-Reply-To:, andReferences:headers - Routing logic maps the address or token to an agent handler
- Agent handler receives the parsed payload and acts
The In-Reply-To and References headers are how email clients thread conversations. Your agent should preserve these in outbound replies — otherwise email clients break the thread display, which degrades the user experience and can trigger spam filters that penalize orphaned messages.
// Constructing a proper reply that maintains thread headers
function buildReplyHeaders(inboundMessage: ParsedEmail): Record<string, string> {
const refs = inboundMessage.references
? `${inboundMessage.references} ${inboundMessage.messageId}`
: inboundMessage.messageId;
return {
'In-Reply-To': inboundMessage.messageId,
'References': refs,
// Prefix subject with Re: if not already present
'Subject': inboundMessage.subject.startsWith('Re:')
? inboundMessage.subject
: `Re: ${inboundMessage.subject}`,
};
}
Platforms like Mails.ai handle the MX delivery, message parsing, and webhook dispatch for you, so you're working with a clean JSON payload rather than raw MIME — which matters when your agent needs to process attachments or multipart bodies.
Sender reputation per agent identity
When each agent has its own sending identity (domain or subdomain), you can measure and protect reputation independently. The metrics that matter:
| Metric | Healthy Range | Action if Exceeded |
|---|---|---|
| Spam complaint rate | < 0.1% (Google threshold) | Pause agent, audit content |
| Bounce rate (hard) | < 2% | Clean address list, check routing logic |
| Bounce rate (soft) | < 5% | Retry with backoff, then remove |
| DMARC failures | 0% | Check SPF/DKIM alignment |
Google Postmaster Tools and Microsoft SNDS give you per-domain reputation signals. If your agent sends to both Gmail and Outlook users at any volume, you want both configured. They're free, and the data isn't available anywhere else.
For agents that send at high volume from a shared IP pool, shared IP reputation drags down even well-behaved senders. A dedicated IP lets you build reputation tied specifically to your agent's sending pattern, and means a misbehaving agent on the same platform doesn't touch you.
Scaling to many agents
When you have dozens or hundreds of agents, manual DNS provisioning breaks down. The scalable approach:
- One wildcard MX on a catch-all subdomain (
*.agents.yourco.ai MX 10 inbound.mails.ai) - One shared DKIM key per sending subdomain (or use per-message signing with a key management service)
- Dynamic address generation using the token-per-thread pattern above
- A registry (Postgres or Redis) mapping agent IDs to their address namespace, token space, and context store
The registry becomes your source of truth. When you spin up a new agent, you register its address namespace and webhook endpoint. When you retire one, you deregister it and archive its message history.
For email infrastructure built specifically for agent workloads — not repurposed transactional email tools — the provisioning API should let you create per-agent inboxes programmatically, not through a dashboard.
Frequently Asked Questions
Can I use Gmail or Outlook to give my agent an email address?
You can, but you'll hit walls quickly. Consumer mail providers rate-limit SMTP sends, don't expose programmatic inbound webhooks, and their Terms of Service typically prohibit automated sending at any real volume. You also can't configure custom DKIM signing or per-subdomain DMARC. For anything beyond a prototype, you need infrastructure built for programmatic access.
How do I handle an agent sending on behalf of a human user?
Use the From: header for the agent's address and set a display name that makes the human's involvement clear (e.g., "Alice via BillingBot" <billing-agent@yourco.ai>). Alternatively, use the Sender: header for the agent and From: for the human — but note that some clients render Sender: inconsistently. The key constraint: From: must be a domain you control and have DKIM-signed, otherwise DMARC will fail.
What's the minimum DNS setup to start receiving inbound email for an agent?
You need an MX record pointing to your inbound processor, and your inbound processor needs to accept mail for that domain. That's technically sufficient for receiving. For sending from the same address, add SPF and DKIM. Add DMARC last, after you've confirmed your sending authentication is working correctly.
How do I prevent one agent's bad behavior from hurting another agent's deliverability?
Subdomain isolation is the primary tool. If billing.agents.yourco.ai develops a spam complaint problem, it doesn't affect onboarding.agents.yourco.ai — they have independent IP reputations (assuming dedicated IPs or at minimum different shared pools) and separate DMARC records. Monitor each subdomain's reputation independently via Postmaster Tools.
Do email clients thread messages from agent addresses correctly?
Yes, if you preserve the standard threading headers. Email clients thread based on Message-ID, In-Reply-To, and References. As long as your agent copies In-Reply-To from the inbound message and appends to the References chain, the thread displays correctly in Gmail, Outlook, and Apple Mail. The agent's address being a machine-generated string doesn't affect threading — it's entirely header-driven.
How do I expire or invalidate a dynamic reply-to address?
Set a TTL on the token in your store (Redis EXPIRE or a Postgres expires_at column). When a reply arrives for an expired token, return a polite bounce or auto-reply explaining the conversation has closed. Don't silently drop it — the sender doesn't know the thread was closed and will be confused by no response.