Blog · Engineering

Giving an AI agent its own email address: a practical guide (SPF, DKIM, DMARC, bounces, threading)

An agent with its own address can sign up for things, answer customers and follow up on its own work. Getting mail to arrive, and replies to land where they should, comes down to a handful of email mechanics.

Published
By
Agentboxd team
Reading time
7 min

Agents are getting email addresses for the same reasons employees do: services send codes to an address, customers answer one, and colleagues want to write to “the research agent” instead of to whoever set it up. The shortcut of giving an agent your own Gmail through OAuth works for a personal assistant, but it mixes your mail with the agent’s, gives the agent years of private history it doesn’t need, and makes every mistake it sends look like it came from you.

A separate address fixes that, and then email itself becomes the problem: authentication, reputation, bounces, threading and loops. This guide goes through each one with what we do at Agentboxd, so you can either rely on it or do the same yourself.

#Choose the domain

There are three sensible options, and one to avoid.

  • A shared agent domain. Our default inboxes live on agents.agentboxd.com. Nothing to set up; the domain’s reputation is ours to keep clean, which we do with the limits described below.
  • A subdomain of yours, such as mail.yourcompany.com. Your brand in the address, your own DKIM key, and a reputation that is separate from your team’s mail.
  • A separate domain for throwaway sign-ups. Sites keep blocklists of disposable-mail domains. We put temporary inboxes on their own domain, tmp.agentboxd.com, so a listing there can never affect permanent agent addresses.
  • Not your root domain. Receiving needs the domain’s MX record to point at the agent’s mail server. On yourcompany.com that would move your whole company’s mail. A subdomain has its own MX, so nothing changes for anyone else.

#Receiving: MX and recipient checks

The MX record says which server accepts mail for a domain. When another server connects to ours on port 25 and says RCPT TO:<research-bot@agents.agentboxd.com>, we look the address up right then. Unknown addresses are refused in the SMTP conversation with 550 5.1.1, instead of being accepted and bounced later. That matters more than it seems: a server that accepts everything and bounces afterwards sends bounces to forged senders (“backscatter”), which gets it blocklisted. Refusing early keeps the sender informed and keeps us out of that.

We also offer STARTTLS, so mail in transit is encrypted with any sending server that supports it, which today is nearly all of them.

#SPF, DKIM and DMARC for sending

When your agent sends mail, the receiving server asks three questions. Getting all three right is what keeps an agent’s mail out of spam. For a custom domain, these are the records we ask you to publish:

DNS for mail.yourcompany.com
mail.yourcompany.com.                 MX   10 mx.agentboxd.com.
mail.yourcompany.com.                 TXT  "v=spf1 include:agents.agentboxd.com -all"
cm1._domainkey.mail.yourcompany.com.  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqh…"
_dmarc.mail.yourcompany.com.          TXT  "v=DMARC1; p=none; rua=mailto:dmarc@agentboxd.com"

SPF: may this server send for the domain?

SPF lists the servers allowed to send mail whose envelope sender (the MAIL FROM in SMTP, which becomes Return-Path) is at your domain. include:agents.agentboxd.com pulls in our sending servers; -all says “nobody else”. A name can have only one SPF record: if you already have one, add the include to it. SPF alone has two weaknesses: it checks the envelope sender, not the From: people see, and it breaks when mail is forwarded, because the forwarder’s server isn’t on your list.

DKIM: was this message signed by the domain?

DKIM signs the message headers and body with a private key; the public half is published in DNS under a selector (for custom domains, cm1._domainkey). A receiving server checks the signature and knows the message wasn’t changed and was signed by that domain. Every message an Agentboxd inbox sends is DKIM-signed, and on a custom domain it is signed with that domain’s own key, not a shared one. Unlike SPF, a DKIM signature usually survives forwarding.

DMARC: does the visible From: line up?

DMARC ties the two to the address people actually see. It passes when SPF or DKIM passes for a domain that aligns with the From: domain. Its policy tells receivers what to do when neither does: p=none (just report), p=quarantine (spam folder) or p=reject. Start at p=none with a rua address for reports, read them for a few weeks, and tighten once you’re sure only your own mail uses the domain. Our temporary domain, which never sends, publishes v=spf1 -all and p=reject, so nobody can send as it at all.

#The same checks, on the way in

Your agent should care about other people’s SPF, DKIM and DMARC too, because a lot of what it receives will claim to be from somewhere it isn’t. Our MX runs all three on every inbound message, records the results in an Authentication-Results header, and labels failures spf-fail and dmarc-fail. Our classifier gets the results as input when it scores phishing. For what an agent should do with those labels, see Prompt injection by email.

#Bounces, complaints and suppression

Sometimes mail can’t be delivered. The receiving server answers with a status code: 4.x.x is temporary (try again later), 5.x.x is permanent. The part that matters for an agent is which permanent failures say something about the address. 5.1.1 (no such user), the other 5.1.x codes and 5.2.1 (mailbox disabled) do: the address is wrong, and sending to it again will only hurt your reputation. A 5.7.1 (“blocked”, “policy”, “spam”) doesn’t: it is about the sender or the content, and the address itself may be fine.

We act on exactly that difference. Address failures put the recipient on a suppression list, the message is marked bounced, and a message.bounced webhook fires. The next attempt to send to that address fails straight away with 422 recipient_suppressed. Policy rejections are recorded but don’t suppress the address. Spam complaints from recipients suppress it too. The delivery-failure report itself arrives in the inbox labelled bounce-report, and doesn’t trigger message.received, so an agent never tries to answer a bounce.

Rates matter as much as single failures. A workspace whose bounce rate goes over 5%, or whose complaint rate goes over 0.1%, over seven days, is paused (403 org_suspended) until the list is cleaned. That sounds strict; it is what protects the reputation every agent on the platform sends with. If your agent writes to addresses it scraped or guessed, those numbers are what you will hit first.

#Threading: Message-ID, In-Reply-To, References

Mail clients decide which messages form a conversation from three headers. Every message has a unique Message-ID. A reply names the message it answers in In-Reply-To, and carries the chain of earlier ids in References:

headers of an agent’s reply
Message-ID: <c1f0…@agents.agentboxd.com>
In-Reply-To: <CAF=x9…@mail.gmail.com>
References: <CAF=a1…@mail.gmail.com> <5e1c…@agents.agentboxd.com> <CAF=x9…@mail.gmail.com>
Subject: Re: Delivery on Friday

Our reply call sets all three, and adds Re: to the subject, so the agent’s answer lands in the other person’s existing conversation in Gmail, Outlook or Apple Mail. On the receiving side we thread by the same headers: In-Reply-To first, then References from newest to oldest. We never thread by subject. “Invoice” from two different customers is two conversations, however similar the subjects look.

The practical rule for agents: answer with the reply call on the message you are answering, not with a new message that has the same subject. A new message starts a new thread in the recipient’s client, and your agent loses the context too.

#Avoid mail loops

Two automated systems answering each other is the classic email failure: an agent replies to an out-of-office, which replies to the agent, and so on until a rate limit ends it. Protect against it in three places. Don’t answer messages with an Auto-Submitted header other than no or with Precedence: bulk, junk or list. Check our ai:auto-reply label, which covers out-of-office replies, ticket acknowledgements and bulk mail. And keep a per-run send budget in the agent’s own tools, so a loop that slips through stops after a few messages instead of a few thousand.

#Warm up and stay within limits

A new sending domain has no reputation. Big mailbox providers are wary of a new domain that suddenly sends a lot, so start with the mail that people want and answer: replies, confirmations, a handful of personal messages. Volume can grow once people have opened and answered. Every Agentboxd plan has a daily cap per workspace and per inbox (the Free plan allows 100 sends a day), and both answer 429 when reached, which is a feature: an agent stuck in a loop hits the cap long before it hurts the domain.

#A checklist

  • Give the agent its own address, on a subdomain if you use your own domain.
  • Publish MX, SPF (one record), DKIM, and DMARC starting at p=none; tighten DMARC later.
  • Refuse unknown recipients during SMTP; never accept-then-bounce.
  • Suppress addresses that hard-bounce with 5.1.x or complain; don’t suppress policy rejections.
  • Reply with In-Reply-To and References; never rely on subjects for threading.
  • Skip Auto-Submitted and bulk mail, and cap sends per run.
  • Check SPF, DKIM and DMARC on inbound mail too, and treat failures as a reason for suspicion.

With Agentboxd, an inbox with all of the above is one API call: see the quickstart, connect a subdomain under custom domains, and pick a framework from the guides.