
To set up DKIM, SPF, and DMARC for a SaaS, you publish three DNS TXT records on your sending domain: an SPF record listing every service allowed to send mail, a DKIM record per provider that holds a public key, and a DMARC record that tells receivers what to do when authentication fails. Ship the DMARC record at p=none first, watch the reports for two weeks, then move to p=quarantine and finally p=reject.
That is the whole job described in 60 seconds. The rest of this post is the version that actually survives a SaaS sending from Resend, Postmark, AWS SES, Customer.io, Stripe, and Intercom on the same apex domain without you discovering at 2am that Gmail is bouncing your password reset emails.
Here are the three records, paste-ready, for a domain example.com that sends transactional email through Resend and marketing email through Postmark.
SPF (one record per domain, lives at the apex or subdomain root):
example.com. TXT "v=spf1 include:amazonses.com include:spf.mtasv.net -all"
DKIM (one record per provider, each on its own selector):
resend._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB..."
20231120m._domainkey.example.com. TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
DMARC (one record per organizational domain):
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com; ruf=mailto:dmarc-forensics@example.com; sp=none; adkim=r; aspf=r; pct=100"
That is the day-one configuration. By week four, the p=none becomes p=reject and sp=none becomes sp=reject. Everything else stays put.
On February 1, 2024, Gmail and Yahoo started enforcing a shared policy: any sender pushing 5,000 or more messages per day to their users must publish SPF, DKIM, and a DMARC record with at least p=none, keep their spam-complaint rate under 0.3%, and offer one-click unsubscribe (the last one became hard-required in June 2024).
Non-compliant senders saw soft-bounces in February, then progressively higher rejection rates from April onward. By summer 2024, "we just won't authenticate, we're small" stopped working as a strategy because the threshold counts your password resets and receipt emails too. A B2B SaaS with 8,000 paying customers crosses 5,000 daily Gmail recipients without any marketing campaign at all.
Microsoft followed in 2025 with a softer version of the same policy. The direction of travel is one-way.
Most teams reach for the obvious setup: one apex domain (example.com) for everything, one SPF record with every provider stuffed into it, one DKIM key from whichever ESP they signed up for first, no DMARC at all.
This works at three providers. It cracks at five. By the time you add a sixth (you will), your SPF record looks like this:
v=spf1 include:_spf.google.com include:amazonses.com include:spf.mtasv.net include:sendgrid.net include:_spf.intercom.io include:mailgun.org include:stripe.com -all
Each of those include: directives can chain into more lookups. RFC 7208 caps SPF at 10 DNS lookups total. Once you blow that, every SPF check returns PermError and your DMARC alignment fails silently across the board. We have seen this break Stripe webhook receipts, vendor invoices, and customer onboarding emails simultaneously, with no obvious symptom except "delivery dropped 40% this week."
The other failure mode is reputation cross-contamination. When marketing blasts a re-engagement campaign and 2% of recipients hit "spam," that complaint rate hits the apex domain reputation, and now your transactional welcome@ emails land in Promotions too. Same domain, same reputation, no way to separate them after the fact.
The pattern that holds up at scale is one subdomain per traffic class, each with its own SPF record, DKIM keys, and DMARC alignment. This mirrors how disciplined teams approach API design contracts in 2026: isolate concerns at the boundary instead of stuffing everything into a shared resource.
Open a spreadsheet. List every system that sends email "from" your domain. Be exhaustive: marketing platform, transactional ESP, support tool (Intercom, Zendesk), billing (Stripe, Chargebee), HR (Rippling, Gusto), CRM (HubSpot), security alerts (Sentry, Datadog), product notifications, and any internal scripts that send via SMTP.
Most SaaS find 8 to 14 senders. You will be surprised by at least two.
send.example.com -> transactional (Resend or Postmark)
news.example.com -> marketing (Customer.io, Loops, Mailchimp)
support.example.com -> ticketing (Intercom, Zendesk)
alerts.example.com -> security and ops (Sentry, internal tools)
billing.example.com -> Stripe, Chargebee
example.com -> human-from-humans only (founder@, hello@)
The apex stays clean. If marketing screws up sender reputation on news.example.com, transactional on send.example.com is unaffected.
Each subdomain gets exactly the providers it uses. Example for send.example.com running on Resend:
send.example.com. TXT "v=spf1 include:amazonses.com -all"
Resend uses Amazon SES under the hood, so its SPF include is amazonses.com. Note the -all (hard fail), not ~all (soft fail). Soft fail is for the rollout phase only; we are past that.
Each provider publishes 1 to 3 CNAME or TXT records during onboarding. The selector pattern varies:
| Provider | SPF include | DKIM selector | Notes |
|---|---|---|---|
| Resend | include:amazonses.com | resend._domainkey | Dashboard generates 3 CNAMEs, paste them as-is |
| Postmark | include:spf.mtasv.net | 20231120m._domainkey (rotates) | Free DMARC digest included with account |
| AWS SES | include:amazonses.com | <token>._domainkey (3 selectors) | Easy DKIM auto-rotates keys every 6 months |
| SendGrid | include:sendgrid.net | s1._domainkey, s2._domainkey | Both CNAMEs required, single-key setup is deprecated |
| Mailgun | include:mailgun.org | mta._domainkey (custom name optional) | Use a domain-specific selector to avoid collisions |
| Customer.io | include:_spf.customer.io | cio1._domainkey, cio2._domainkey | Marketing subdomain only |
Verify each key with dig TXT resend._domainkey.send.example.com +short after publishing. If you see the p= value echoed back, the record is live. DNS propagation is usually under 5 minutes on Cloudflare or Route 53; allow 30 minutes on slower registrars.
The _dmarc record sits at the subdomain root, one per zone:
_dmarc.send.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r; pct=100"
_dmarc.news.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r; pct=100"
_dmarc.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; sp=none; adkim=r; aspf=r; pct=100"
The sp= tag on the apex is the subdomain policy: it controls what DMARC does for any subdomain that doesn't have its own record. Set sp= explicitly so you don't get surprised when someone provisions staging.example.com next quarter.
For 14 days, read the daily DMARC aggregate reports. You will discover senders you forgot, third-party tools spoofing your domain, and at least one internal cron job sending from the wrong subdomain. Fix each one by either authorizing it (add to SPF, give it DKIM keys) or shutting it down.
Once the report shows 99%+ of legitimate mail passing alignment, move:
p=none -> p=quarantine; pct=25
(wait 1 week, watch reports)
p=quarantine; pct=100
(wait 1 week)
p=reject; pct=100
The pct tag means "apply this policy to this percentage of failing mail." Ratcheting from 25% to 100% catches edge cases without taking down a forwarder for everyone at once.
Raw DMARC aggregate reports arrive as gzipped XML files attached to email. Nobody reads XML for fun. You need a parser.
| Tool | Cost | What you get |
|---|---|---|
| Postmark DMARC Digests (free) | $0 | Weekly human-readable email; top 10 sources, top 5 IPs per domain |
| Postmark DMARC Digests (paid) | from $99/mo | Web dashboard, 60-day history, all sources |
| Dmarcian | from $24/mo | Real-time dashboard, threat intel, multi-domain |
| EasyDMARC | from $35/mo | Includes BIMI/MTA-STS tooling |
| Valimail Monitor | free tier; enterprise paid | Solid free tier, paid for enforcement automation |
For most SaaS under 50 employees, Postmark's free weekly digest is genuinely sufficient. You give them a domain, they give you an email address to put in your rua= tag, and a week later you start receiving readable summaries. We use it on production domains today.
Pay for Dmarcian once you have more than three domains, or once compliance requires source-level audit trails (relevant if you are early in a SOC 2 audit preparation cycle, where DMARC enforcement evidence shows up under common-criteria controls).
SPF flattening. Tempting fix when you blow the 10-lookup limit: replace include: directives with literal IP ranges. Symptom: works for two weeks, then a provider rotates IPs and authentication starts failing for that provider only. Don't flatten; subdomain-isolate instead.
Missing sp= tag. You publish DMARC at p=reject on the apex but forget the subdomain policy. Symptom: someone spins up mktg.example.com six months later, sends from it, gets full delivery, and you have no idea your enforcement isn't actually enforced.
Going to p=reject before fixing forwarders. Mailing lists and forwarders break SPF alignment by design. If your customers forward your transactional mail to a personal address through Gmail's "send mail as" feature, those messages can fail SPF. Symptom: angry support ticket from one specific user. Fix by relying on DKIM alignment (which survives forwarding) and only enabling p=reject once your DKIM-pass rate is near 100%.
Forgetting DKIM key rotation. A 1024-bit DKIM key from 2019 still works but is now considered weak. Yahoo started flagging short keys in 2025. Rotate to 2048-bit annually; AWS SES and Postmark do this automatically if you enable Easy DKIM.
BIMI without a VMC. BIMI lets your logo render next to your sender name in Gmail and Yahoo. The catch: Gmail requires a Verified Mark Certificate (VMC) from DigiCert or Entrust, which costs $1,000 to $1,500/year, requires a registered trademark, and takes 4 to 8 weeks to issue. For consumer-facing SaaS with brand-recognition value, the math works. For B2B tools, skip it.
If you are two founders pre-revenue, sending under 100 emails a day from a single provider, you do not need the subdomain matrix. Publish SPF and DKIM on the apex, set DMARC to p=none for monitoring, and revisit when you cross 1,000 emails/day or hire your second engineer.
The boundary is roughly the same as when you should stop running everything on Vercel and start thinking about a real backend split, which we wrote about in how to choose a tech stack for your startup in 2026. Best practices have ROI curves; respect them.
If you are an internal-only tool that never sends mail outside your VPC: skip DMARC entirely, do nothing. Email authentication only matters when receivers care.
The reason most SaaS teams put this off is that the rollout is not hard but it is fiddly. Mapping 12 senders, publishing 30+ DNS records across 6 subdomains, monitoring DMARC reports daily for two weeks, debugging the one cron job sending from the wrong selector: it is two to three weeks of focused engineering work that nobody on a 4-person team has bandwidth for.
A senior engineer on Cadence at $1,500/week handles the entire rollout end-to-end (subdomain plan, all DNS records, DMARC monitoring setup, ratchet schedule), typically in 2 weeks. Every engineer on Cadence is AI-native by default, vetted on Cursor and Claude Code fluency before they unlock bookings, which matters here because most of this work is the kind of pattern-matching ("here are 14 sender configs, generate the matching DNS records") that AI tools handle in minutes rather than hours. Our matched pool is 12,800 engineers across 40 countries; the matching algorithm scores all of them in 80ms per booking and returns the top 4. If you want a working DMARC p=reject posture by end of month, book a senior engineer through the Cadence onboarding flow and start the 48-hour free trial.
Skip this if you have a deliverability lead in-house already. They will do it better than a contractor would.
Plan four weeks end-to-end: one week to map senders and publish records at p=none, two weeks of monitoring and fixes, then one week to move through p=quarantine to p=reject. Rushing it is the most common cause of "we set up DMARC and our delivery dropped."
Yes. Gmail and Yahoo apply the same rules to transactional senders the moment you cross 5,000 messages a day to their users, and a B2B SaaS with active customers crosses that threshold faster than founders expect. Even at low volume, DMARC at p=none is free spoofing protection.
The DNS records themselves cost nothing. Add $0 to $50/month for DMARC report aggregation depending on whether Postmark's free digest is enough or you need Dmarcian. BIMI adds $1,000 to $1,500/year for the VMC certificate and is only worth it for consumer brands with logo equity.
Publish SPF and DKIM on day one (it is 15 minutes of work and protects you against trivial spoofing). Skip DMARC enforcement until you cross 1,000 emails/day or someone tries to spoof you. p=none monitoring is still cheap insurance.
No, not at scale. SPF caps at 10 DNS lookups per RFC 7208, and most SaaS hit that wall around 5 to 7 third-party senders. Subdomain isolation is the only sustainable fix; SPF flattening is a trap that works for a fortnight then silently breaks when a provider rotates IPs.
Audit your stack in 10 minutes. If you are not sure whether your current SPF setup will survive your next ESP addition, run your stack through Ship or Skip for an honest grade on what to fix first.