Authentication failures are the fastest way to destroy deliverability for an AI agent sender. When your agent sends a notification, reply, or workflow update and the receiving MTA silently drops it into spam—or rejects it outright—the root cause is almost always a broken SPF record, an invalid DKIM signature, or a DMARC policy mismatch. This guide explains the mechanics of each layer, shows you how to read the diagnostic headers, and gives you a step-by-step debugging process to fix failures before they compound into a reputation problem.
How SPF, DKIM, and DMARC actually work together
SPF and DKIM are independent authentication mechanisms. DMARC ties them together by requiring that at least one of them aligns with the RFC5322 From: domain. Understanding alignment is the key to debugging most failures.
SPF authorizes sending IPs. A receiving MTA looks up the TXT record for the domain in the envelope MAIL FROM (also called the Return-Path), then checks whether the connecting IP appears in the authorized list. SPF validates the envelope sender, not the header From:. This distinction matters enormously for agents.
DKIM attaches a cryptographic signature to the message. The DKIM-Signature header declares a signing domain (d=) and selector (s=). The receiver fetches the public key from DNS at s._domainkey.d and verifies the signature over a canonicalized subset of headers and the body. If the message is modified in transit—by a mailing list that appends a footer, or a relay that re-wraps lines—the signature breaks.
DMARC adds a policy layer on top. The receiving MTA checks the _dmarc.yourdomain.com TXT record to find out what to do when both SPF and DKIM fail (or fail to align). The p= tag sets the policy: none (monitor only), quarantine (spam folder), or reject (bounce).
Identifier alignment
Alignment is what trips up automated senders most often. DMARC requires that either:
- The SPF-authenticated domain (the envelope
MAIL FROMdomain) matches theFrom:header domain, or - The DKIM
d=signing domain matches theFrom:header domain
"Matches" means exact domain by default (strict), or organizational domain if the DMARC record specifies aspf=r or adkim=r (relaxed). Most records use relaxed, so mail.example.com aligns with example.com.
When an AI agent sends from a shared infrastructure domain but the From: header shows a custom domain, neither SPF nor DKIM may align, and DMARC will quarantine or reject the message regardless of whether both mechanisms technically pass.
Reading Authentication-Results headers
Every major receiving MTA stamps an Authentication-Results header on inbound mail. This is your primary debugging instrument. When an agent's sent mail bounces or lands in spam, forward the full headers from a test delivery to your own Gmail or similar account.
A passing result looks like this:
Authentication-Results: mx.google.com;
dkim=pass header.i=@yourdomain.com header.s=selector1;
spf=pass (google.com: domain of return-path@send.yourdomain.com designates 203.0.113.42 as permitted sender) smtp.mailfrom=return-path@send.yourdomain.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=yourdomain.com
A failing result with DMARC rejection looks like:
Authentication-Results: mx.google.com;
dkim=fail (signature did not verify) header.i=@yourdomain.com;
spf=pass smtp.mailfrom=shared-infra.mails.ai;
dmarc=fail (p=REJECT) header.from=yourdomain.com
Notice that SPF passed here (the IP is authorized for shared-infra.mails.ai) but DMARC still failed because the SPF domain doesn't align with yourdomain.com in the From: header, and DKIM verification failed independently.
Key fields to parse:
| Field | What to look for |
|---|---|
dkim= |
pass, fail, permerror, temperror, none |
header.i= |
The signing domain (must match or align with From:) |
spf= |
pass, fail, softfail, neutral, permerror |
smtp.mailfrom= |
The envelope Return-Path domain |
dmarc= |
pass, fail, bestguesspass |
header.from= |
The RFC5322 From: domain DMARC evaluated |
Debugging SPF failures
SPF failures for agent senders typically fall into three categories.
1. The sending IP isn't listed
If you're sending through an email platform, their sending IPs must appear in your SPF record. Add a custom sending domain without updating your SPF record and every message will fail SPF.
Check your current record:
dig TXT yourdomain.com | grep spf
A typical record for a platform-routed sender:
v=spf1 include:spf.youremailplatform.com ~all
The include: mechanism delegates to the platform's published SPF record. The final qualifier matters: ~all is softfail (messages may still deliver), -all is hardfail (messages from unlisted IPs fail SPF). Never use +all.
2. Too many DNS lookups (PermError)
SPF has a hard limit of 10 DNS lookups per evaluation. include:, a, mx, ptr, and exists mechanisms each count. Nest multiple include: directives pointing to platforms that themselves chain several includes, and you'll hit the limit. Receivers then return permerror, which DMARC treats as SPF failure.
Audit your lookup count:
npm install -g spf-flattener
spf-flattener yourdomain.com
Flatten the record by resolving all includes to their current IPs and replacing the includes with explicit ip4: and ip6: mechanisms. The downside: you must re-flatten whenever the platform adds IPs. Automate this with a CI job.
3. SPF alignment with shared infrastructure
This is the scenario described earlier. When you send through a shared infrastructure provider, the envelope MAIL FROM will be a subdomain of the provider's domain, not yours. SPF passes for the provider's domain but doesn't align with your From: header domain for DMARC purposes. The fix is to verify a custom domain with the provider, so your Return-Path can be something like bounce.yourdomain.com and SPF alignment holds.
Debugging DKIM failures
DKIM failures are either configuration problems or transit modification problems.
Verify the DNS record exists
dig TXT selector1._domainkey.yourdomain.com
Expected output shape:
selector1._domainkey.yourdomain.com. 300 IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w..."
NXDOMAIN or SERVFAIL means the key isn't published. An empty p= value means the key has been revoked. Both cause permerror.
Check the signature header
In the raw message source, find the DKIM-Signature header:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=yourdomain.com; s=selector1;
h=from:to:subject:date:message-id;
bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
b=base64signaturedata...
Key fields:
d=— must align withFrom:for DMARCs=— selector; must have a matching DNS record ats._domainkey.dh=— the signed headers;frommust be includedc=— canonicalization;relaxed/relaxedis most forgiving of minor header modificationsbh=— body hash; if anything in the body changes after signing, this won't match
Body hash mismatches
dkim=fail (body hash did not verify) means something modified the message body in transit. Common culprits:
- Mailing list software that appends unsubscribe footers
- Spam filters that add disclaimers
- Line-length normalization by intermediate relays (less common with
relaxedcanonicalization) - HTML minification applied after signing
If your agent sends to a mailing list and you see this failure, it's the list software, not your code. For direct delivery, body modification after signing shouldn't happen. If it does, check whether any middleware in your sending pipeline transforms the message after the DKIM signature is computed.
Key length and algorithm
RSA-1024 keys are still technically valid but many receivers flag them. Use RSA-2048 minimum. Some providers have moved to ED25519 (k=ed25519), which is shorter and faster to verify, though a small number of older MTAs don't support it. If you need maximum compatibility, sign with both and include two DKIM-Signature headers.
Debugging DMARC failures
If SPF and DKIM are individually passing but DMARC still fails, the issue is alignment, not the mechanisms themselves.
Check your DMARC record
dig TXT _dmarc.yourdomain.com
A typical record:
_dmarc.yourdomain.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com; pct=100; adkim=r; aspf=r"
No _dmarc record at all means DMARC evaluation returns none and no policy is applied. This won't cause failures, but you're flying blind. A record with p=reject means any alignment failure will bounce the message.
DMARC report analysis
Aggregate reports (rua=) arrive daily as compressed XML files. They contain per-IP, per-sending-source breakdowns of pass/fail counts. Set up a report receiver early, even a simple forwarding address you parse manually, so you catch partial failures before the full volume of agent sends compounds the problem.
Free tools like Google Postmaster Tools (for mail to Gmail) and dmarcian's report viewer can parse these for you.
Subdomain policy
The sp= tag sets policy for subdomains. If your From: uses noreply@sub.yourdomain.com and your DMARC record lives at _dmarc.yourdomain.com with sp=reject, the subdomain inherits the reject policy. Make sure you're publishing the DMARC record at the right level for the domain in your From: header.
A practical debugging checklist for agent senders
When an agent's email isn't delivering:
- Send a test message to a Gmail address you control. View the full source (
Show originalin Gmail). CheckAuthentication-Results. - Identify which mechanism failed — SPF, DKIM, or DMARC alignment.
- For SPF failures: Confirm the sending IP is in the SPF record for the envelope domain. Count lookup depth. Check alignment with
From:domain. - For DKIM failures: Verify the DNS record exists and has a valid key. Check if
d=aligns withFrom:. Check for body modification. - For DMARC failures with passing mechanisms: Check alignment mode (
aspf=,adkim=). Verify theFrom:domain matches what DMARC evaluated. - Check DMARC aggregate reports to identify which sources are failing and at what rate.
- Verify with a DNS propagation checker after any record change — TTL-cached stale records cause confusing intermittent failures.
Platforms designed for agent sending, like Mails.ai, handle DKIM signing and SPF alignment automatically when you verify a custom domain — the DNS records to publish are returned directly from the domain registration API, so there's no manual assembly of SPF includes or DKIM selectors.
Common failure patterns for AI agent senders
Agent email workloads hit a few specific failure patterns that transactional email doesn't typically surface.
Multiple-agent, single-domain sending. When several agents share one custom domain in From: but route through different infrastructure paths, you can end up with DKIM signed by different selectors, or no DKIM at all for some paths. Audit every code path that calls your send API and confirm all of them produce a signed message.
Reply threading changes From:. An agent that replies to inbound email might copy the original From: or construct a new one with a different domain. If the reply From: changes domain, your DMARC record may not cover it. Use a consistent From: address and control it programmatically — see Mails.ai's inbound email parsing for how to handle reply-to threading without corrupting the sender identity.
Forwarded messages. If your agent processes inbound mail and re-sends it to another address, you're now the originating MTA for a message whose content you didn't create. ARC (Authenticated Received Chain) is the mechanism for preserving authentication state through forwarding hops, but most senders don't need to implement it directly — your sending platform should handle it.
High-volume, rapid sends. SPF lookups are cached per TTL, but hitting new receivers at high rate can surface SPF permerror from lookup-chain depth issues that only appear under load. Flatten your SPF record and test with the actual sending IPs before scaling.
Frequently Asked Questions
Why does DMARC fail even when SPF and DKIM both pass?
DMARC requires alignment, not just passing. SPF must pass for a domain that matches the From: header domain (or its organizational parent under relaxed alignment). If SPF passes for send.yourplatform.com but your From: says yourdomain.com, alignment fails even though SPF technically passed. The same applies to DKIM: d= must align with From:. Check header.from= versus smtp.mailfrom= in your Authentication-Results header.
How do I fix SPF PermError from too many lookups?
Flatten your SPF record. Resolve all include: directives to their current IP ranges and replace them with explicit ip4: and ip6: mechanisms. This eliminates DNS lookups at evaluation time. Automate re-flattening in CI because platform IP ranges change. Tools like spf-flattener or dmarcian's SPF surveyor enumerate your current lookup depth.
Why does DKIM verification fail only sometimes?
Intermittent DKIM failure usually means transit modification — the body hash doesn't match because something is changing the message after it's signed. Check whether any intermediate relay, spam filter, or mailing list software is appending content. If you're seeing this in direct SMTP delivery with no intermediaries, check whether your sending platform signs before or after any content transformation it applies.
My DMARC record is p=none — am I safe?
p=none means you're in monitoring mode. Mail that fails DMARC is still delivered — receivers just report it back via aggregate reports. This is fine for initial rollout, but it doesn't protect you from spoofing and it doesn't tell you definitively whether your legitimate mail is passing authentication. Use it to gather a baseline from aggregate reports, then move to p=quarantine and eventually p=reject once you've confirmed all your legitimate sending paths pass alignment.
Does DKIM use the From: address or the envelope sender?
Neither, directly. DKIM signs using the domain declared in the d= tag of the DKIM-Signature header. That signing domain is what DMARC compares against the RFC5322 From: header for DKIM alignment. The envelope sender (Return-Path) is irrelevant to DKIM. This is why you can have DKIM pass and SPF fail simultaneously on the same message.
Should AI agent senders use p=reject from day one?
No. Start with p=none, send test messages to major providers (Gmail, Outlook, Yahoo), and read your aggregate reports for at least a week. Confirm that all sending paths — your main agent sends, replies, forwarded messages, automated notifications — are producing aligned, signed messages. Only then step to p=quarantine. Move to p=reject once aggregate reports show near-zero failure rates from your own infrastructure. See email deliverability for automated senders for more on managing reputation alongside authentication.
Related guides: SPF Alignment Failures for Programmatic Senders, Email Unsubscribe Header Handling for AI Agents, Handle Bounce & Complaint Webhooks Automatically | Agent Email and SPF DKIM DMARC for Automated Email Senders.
Explore the product: Inbound email API, Sender reputation, All features and Pricing.
