Inboxes · September 29, 2026
DMARC Policy for an Autonomous Email Sender
DMARC for AI agent senders: why a fresh agent subdomain should start at p=reject, when that flips, and what sp, np and alignment actually cover.
Most DMARC guides give the same schedule: publish p=none, read aggregate reports for
a few weeks, move to p=quarantine, read more reports, and only then publish
p=reject. For a company domain with a decade of mail servers, CRMs and forgotten
invoicing tools behind it, that schedule is sound. For an agent that sends from a
subdomain created last Tuesday, it is weeks in which anyone can put the agent’s address
in a From: header and the domain owner has asked receivers to do nothing about it.
This piece argues for DMARC for AI agent senders at p=reject from the first message on
a dedicated subdomain, gives the record tag by tag, and lists the cases where the
recommendation flips. It is about policy only. Publishing the ownership, SPF, DKIM and MX
records for the subdomain is covered in
running agent email on a subdomain.
One limit first. Custom domains on this product require a paid plan, and paid plans
cannot be bought yet. Today every live inbox sends from agents.emailforagents.ai, a
domain the platform operates, so its DMARC record is the platform’s to set, not yours.
Everything below applies to a subdomain you control and send from through another
provider today; the product-specific sections describe what this code does once a
domain is attached, which no one can do yet.
DMARC for AI agent senders: why the monitoring phase protects nothing
Monitoring mode exists to find senders you forgot about, and a new subdomain has none
to find. That is not an interpretation; it is the stated reason. RFC 9989, the
Proposed Standard published in May 2026 that obsoletes RFC 7489 and RFC 9091, explains
in section 5.1.5 that the reason for starting at p=none “is to ensure that nothing’s
been missed in the initial SPF and DKIM deployments”, because a domain owner “can
overlook a server here or be unaware of a third-party sending agreement there.”
Apply that to agents.example.com on the day you create it. There is no server to
overlook, because no server has ever sent as that name. There is no third-party
agreement, because nobody else knows the name exists. The complete list of legitimate
sources is the one you are configuring right now.
Meanwhile the monitoring phase has a real cost. RFC 9989 section 5.4 says discovered
p=none policies “MUST NOT modify existing mail handling processes.” During those weeks
the agent is introducing a brand-new address to suppliers, customers and other agents,
all learning to trust a name that is, by the owner’s own declaration, unprotected.
Reports do not require monitoring mode. A rua tag works identically at p=reject. You
still receive the aggregate XML from every receiver that sends it, and anything
unexpected shows up there. The only difference is that its mail was refused while you
looked.
This is a deliberate departure from mainstream advice, not a restatement of it.
Microsoft’s DMARC setup guide for Microsoft 365, checked on 29 September 2026, tells you
to start even a low-volume subdomain with few sources at p=none, then step through
quarantine with pct increments. Its only immediate v=DMARC1; p=reject; is for
parked domains that never send. A fresh agent subdomain sits between those cases: it
will send, but through exactly one path you can enumerate.
On this product that path is enumerable in code, not just in principle. An inbox on a
custom domain cannot be created until ownership, sending and receiving are all ready,
or it fails with HTTP 422 domain_not_ready. Sending is ready only when the provider
reports the domain verified and every SPF and DKIM record, at least one of each, is
verified. So nothing sends from your subdomain before a DKIM key for it is verified.
The record to publish, tag by tag
Publish one TXT record at _dmarc under the agent hostname. For a subdomain whose DKIM
key the console shows at <selector>._domainkey.agents.example.com:
_dmarc.agents.example.com. TXT "v=DMARC1; p=reject; adkim=s; rua=mailto:dmarc-reports@example.com"
Check it from a shell before and after, and confirm there is exactly one:
dig +short TXT _dmarc.agents.example.com
dig +short TXT _dmarc.example.com
Both specifications discard your policy if more than one DMARC record comes back for the
name. An RFC 7489 receiver then applies no DMARC at all; an RFC 9989 receiver keeps
walking and applies whatever the parent publishes. Either way a leftover template record
silently replaces your p=reject.
| Tag | Value | Why |
|---|---|---|
v | DMARC1 | Required, and must come first |
p | reject | Nothing legitimate to protect with a softer value |
adkim | s, if the key is under the hostname | The signing domain is the hostname itself; strict refuses signatures from the parent or sibling names |
aspf | omit (defaults to r) | The bounce address is on a child name, so strict SPF alignment can never pass |
rua | a mailbox you read | Your only view of what receivers saw |
sp | omit | Read only if this name ends up as the organizational domain; see below |
pct, t | omit | Testing knobs; see below |
Keep SPF alignment relaxed. The sending provider behind this product puts the envelope
sender on a subdomain: Resend’s documentation states that “by default, Resend uses the
send subdomain for the Return-Path address”, and the domain-creation request this
product makes sets no custom return path. A captured message from the platform shows the
result: the Return-Path is an address at send.agents.emailforagents.ai, while the
From: is at agents.emailforagents.ai. Under relaxed alignment those share an
organizational domain and align. Under aspf=s they never do, which silently removes SPF
as a second route to a DMARC pass. That second route matters on direct delivery: if the
DKIM signature fails on a message that came straight from the provider, aligned SPF still
carries it.
Strict DKIM alignment is safe only if the signing domain is the hostname. DKIM looks
up its key at selector._domainkey.<d=>, so the host of the DKIM record the provider
issues tells you the d= value. The platform’s own key is published at
resend._domainkey.agents.emailforagents.ai, so signatures made with it carry
d=agents.emailforagents.ai, an exact match for its From: domain. If the DKIM record
you are handed sits anywhere else, leave adkim at its relaxed default.
Skip pct and t. RFC 9989 removes pct (Appendix A.6; the tag registry now lists
it as historic), because values other than 0 and 100 were applied inconsistently, and
adds t=y as the testing flag. Neither behaves uniformly in 2026. An RFC 7489 receiver
ignores unknown tags, so it ignores t=y and applies your full p; an RFC 9989 receiver
does not process pct. A hedge that some receivers ignore is not a hedge. Microsoft’s
guide still calls RFC 7489 the exhaustive description of the record; on a sender with one
source there is nothing to sample anyway.
What a subdomain record does not cover
A record at _dmarc.agents.example.com governs mail whose From: domain is exactly
agents.example.com, and nothing below it. The isolation boundary is real: a
subdomain’s own record overrides whatever the parent says, including its sp, so the
agent’s policy does not depend on the company’s. But two kinds of neighbouring name fall
outside it.
The first is the bounce host. send.agents.example.com exists, since it carries the SPF
and MX records the provider needs, and it is a perfectly good From: domain for someone
who wants to impersonate the agent. It has no DMARC record of its own. The second is any
fabricated deeper name, such as billing.agents.example.com.
Where those names find their policy depends on which DMARC the receiver implements.
- RFC 7489 receivers query at most two names: the
From:domain, then the organizational domain, which isexample.com. Intermediate names are skipped, so_dmarc.agents.example.comis never consulted forbilling.agents.example.com. Resend’s explainer on subdomain policy, published in June 2025, describes precisely this two-query rule. It was correct for the specification in force when it was written. - RFC 9989 receivers walk the tree, querying
_dmarcat each parent up to eight names. The walk now finds your agent record, but absent apsdtag the organizational domain is the name with the fewest labels that has a record, so ifexample.compublishes one, its policy is applied:spfor a subdomain that exists, and the newnptag for one that does not. A name counts as non-existent when the query for it returns NXDOMAIN.
Either way, if the parent publishes a record, the parent decides for those names. Two practical consequences:
- Publish a second record for the bounce host.
_dmarc.send.agents.example.comwithv=DMARC1; p=rejectcosts one TXT record. Nothing legitimate uses that name in aFrom:header, and it does not disturb alignment for your real mail. - If you control the parent, add
np=rejectthere. It closes fabricated names under every subdomain without touching the policy on subdomains that exist, which is usually the part of a company zone nobody wants to change this quarter. Receivers that do not yet implementnpfall back to the parent’ssp, or itsp.
Do not rely on sp=reject in the agent record to cover deeper names. RFC 7489 says sp
“will be ignored for DMARC records published on subdomains of Organizational Domains”,
and RFC 9989 keeps the rule, with one exception: if the parent publishes no DMARC record,
the tree walk makes agents.example.com the organizational domain for its children and
its sp and np apply. An RFC 7489 receiver gives those names no DMARC evaluation at
all, so the parent record is the one that works everywhere.
When the recommendation flips
The p=reject default rests on one premise: you know every sender of the name. Where that
premise fails, so does the recommendation.
| Situation | Start at | Reason |
|---|---|---|
| New label; only the agent platform sends; agent mails individuals | p=reject | Nothing for monitoring to discover |
| The label already sends through a helpdesk, CRM or bulk tool | p=none with rua | The discovery phase has something to discover; or pick a fresh label |
| The agent posts to mailing lists or open discussion groups | p=quarantine, or restrict | See the next section |
You are tempted to set aspf=s | Leave it relaxed | The bounce address is on send., so SPF alignment can only fail |
| The DKIM record is not under the agent hostname | Leave adkim relaxed | Strict alignment would fail every signature |
The parent has p=none and no np | p=reject on the agent name, and fix the parent if you can | The agent name is protected; its neighbours are not |
The agent sends from agents.emailforagents.ai | Nothing to publish | The platform owns that record |
The second row is the one people talk themselves out of. “We only use it for the agent”
is often true of intent and false of history. Before publishing p=reject on a name you
did not create this week, look for old SPF includes, DKIM selectors or third-party
verification records at it. Each is a sender monitoring mode exists to find. If the list
is long, a new label such as agent-mail.example.com is cheaper than the audit.
The last row is today’s actual situation. As of 29 September 2026, dig TXT _dmarc.agents.emailforagents.ai returns
v=DMARC1; p=none; rua=mailto:support@agents.emailforagents.ai; adkim=r; aspf=r; ri=86400. Say it plainly: the platform does not follow this article’s advice. Its name
has one sending path and a DKIM key under the hostname, which is exactly the case argued
for p=reject above, and it still publishes p=none, so it asks receivers to do nothing
about mail that forges an agent’s address. You cannot change that record, and nothing
here promises when it will change. The
agent email threat model lists it as an open gap in
sender impersonation.
Forwarding, mailing lists and the agent in a thread
Plain forwarding is not a reason to back off p=reject; mailing lists that edit
messages are. The difference is which authentication survives.
When a recipient’s mailbox forwards your agent’s message somewhere else, the final hop
comes from the forwarder’s IP, so SPF fails. DKIM usually survives, because the body and
signed headers arrive unchanged. RFC 9989 section 7.4 turns this into a requirement:
domains publishing p=reject “MUST NOT rely solely on SPF to secure a DMARC pass and MUST
apply valid DKIM signatures.” Sending on this product cannot become ready without a
verified DKIM record. Note also what reject is: a request. Section 5.4 says receivers
SHOULD NOT reject solely because of it, and many weigh it with their own analysis.
Mailing lists are different. A list that adds a subject tag or a footer breaks the DKIM
signature, and the list server is not in your SPF. Most list software now rewrites the
From: to the list’s own address, which RFC 9989 describes as firmly established; a list
that does not delivers copies that fail DMARC, and p=reject asks subscribers’ receivers
to refuse them. The same section says domains that “host users who might post messages
to mailing lists SHOULD NOT publish” p=reject, and warns that rejections can get the
list’s other subscribers unsubscribed by the list software’s own bounce handling.
That clause was written for domains full of people, who join lists unpredictably. An agent’s recipients are decided by code and a model, which you can constrain. So the choice is explicit:
- If the agent’s job includes posting to a public list, a community forum’s email gateway
or a group alias that rewrites messages, start at
p=quarantine, watch the reports, and accept that list delivery will be imperfect at any enforcing policy. - If it does not, keep
p=rejectand make “does not” enforceable. An inbox’s send policy on this product acceptsblock_domainsandallow_domains, and restricting which domains an agent can email covers how to use them. That is a stronger control than a DMARC exception, because it stops the message before it is sent rather than after it is rejected.
ARC helps only where the final receiver has chosen to trust the intermediary that sealed the message. That is their decision, not a record you can publish.
What the domains API checks, and what it never reads
This product does not read, check, require or suggest a DMARC record. That is a statement about source, not an omission from the docs. What it does report is in the custom domains reference. Here is each of the four endpoints.
POST /projects/{project_id}/domains/previewresolves MX at the hostname and returns a planned record list without storing anything. The plan has three rows: an ownership TXT at_efa-challenge.<hostname>, a receiving MX, and a sending TXT whose note says SPF and DKIM values appear after ownership verifies. There is no DMARC row.POST /projects/{project_id}/domainsattaches the hostname. It needs a live project and a verified paid subscription; without one it returns HTTP 402payment_required. That is the response everyone gets today, because paid plans cannot be bought.GET /projects/{project_id}/domains/{domain_id}returnsownership_status,sending_status,receiving_statusand the records, each with itstype,host,purpose,statusandprovider_record_kind.POST /projects/{project_id}/domains/{domain_id}/verifyre-checks. It also requires the paid plan. Its only TXT lookup is the ownership challenge, compared by exact string. Sending readiness comes from the provider’s domain status and its verification of the SPF and DKIM records.
The resolver interface the domain code uses has two methods, MX and TXT, and neither is
ever pointed at a _dmarc name. A missing DMARC record will not hold a domain in
pending, and a broken one will not be reported. Check your own with dig.
# Requires a live project on a paid plan, which cannot be bought yet.
curl -s "https://api.emailforagents.ai/v1/projects/$EFA_PROJECT_ID/domains/$EFA_DOMAIN_ID" \
-H "Authorization: Bearer $EFA_API_KEY" |
jq '{ownership_status, sending_status, receiving_status,
records: [.records[] | {type, host, purpose, status, provider_record_kind}]}'
The one domain event is domain.updated. It fires when a domain is attached, after every
ownership check, and after every provider readiness check. Every payload carries
domain_id and mutates_customer_dns: false. The attach and ownership events add
ownership_status, the attach event also adds receiving_mode and migration_consent,
and the readiness event carries all three statuses. None mentions DMARC. There is no
domain.verify event; that string names the background job that re-checks domains.
One ordering detail matters if you drive setup from events. When an explicit check asks
the provider to restart verification, sending_status drops back to pending after the
readiness event has gone out, and no second event follows; a failed background re-check
does the same. Treat domain.updated as a signal to GET the domain, not as its state.
Checking the first message end to end
A published record is a claim. The first real message is the evidence. Send one from the
agent to a mailbox you control at a large provider, open the raw headers, and find the
Authentication-Results line the receiver added. For a correctly configured
agents.example.com it should read along these lines:
Authentication-Results: mx.receiver.example;
dkim=pass header.d=agents.example.com;
spf=pass smtp.mailfrom=send.agents.example.com;
dmarc=pass header.from=agents.example.com
header.d equal to header.from means strict DKIM alignment holds. smtp.mailfrom on
send. is why aspf stays relaxed. If something is wrong you get a rejection or a junk
copy instead of a pass, and a failure in your own test mailbox, before the agent has
written to anyone who matters, is the cheapest DMARC failure you will ever get.
Then give the aggregate reports a week. On an isolated agent subdomain they should be boring: one source, aligned, passing. Anything else is either a forgotten sender, meaning the name was not as fresh as you thought, or someone using the agent’s name who has just been refused.
None of this is a delivery result. A passing DMARC evaluation is the entry fee for being judged on reputation and content, not a verdict: a 550 5.7.1 bounce can still arrive on perfectly authenticated mail, and why agent email lands in spam covers the filtering that happens after authentication passes. For where DMARC sits among everything else an agent inbox needs, see email for AI agents and the agent inbox overview.
Keep reading
Agent Email on a Subdomain Without Breaking Company Mail
Run agent mail on agents.yourcompany.com: inspect existing MX, publish ownership, SPF, DKIM and receiving records, and leave employee routing untouched.
Agent Email Bounced 550 5.7.1: What It Means
Agent email bounced 550 5.7.1? Which variant you hit, why it is rarely your DNS on a shared domain, and why the agent must stop, not reword and resend.
The Agent Email Threat Model
Assets, actors, trust boundaries and eight threats for an agent with an email address, with the layer where each control binds and where ours stops short.