Email Deliverability Runbook (SPF, DKIM, DMARC)
Status: DRAFT runbook. This document tracks what must be configured at the DNS provider for
attunelogic.comoutbound mail. Many of the steps below require access to the production DNS zone and the upstream mail provider (e.g. SendGrid, Mailgun, AWS SES, Postmark) and so cannot be applied from the code base alone. Treat the checklist as the source of truth and tick items off as they are completed in production DNS.
Why this mattersβ
Without proper SPF, DKIM, and DMARC records, the API's outbound mail
(src/services/mailer/...) is highly likely to be:
- silently dropped by Gmail / Microsoft 365 (the two providers our customers use most),
- placed into the spam folder, or
- bounced with a
550 5.7.xpolicy error that we never see in Sentry because it is the recipient that rejects the message.
Production password-reset, invite, and approval emails depend on this working.
The provider auditβ
Before changing anything, identify the actual sending path in production.
# from a machine that can reach prod DNS
dig +short MX attunelogic.com
dig +short TXT attunelogic.com # SPF lives here
dig +short TXT _dmarc.attunelogic.com # DMARC lives here
dig +short TXT <selector>._domainkey.attunelogic.com # DKIM
Record the answers in a private ops doc (1Password / Notion). Anything below that contradicts the live DNS should win against this runbook.
Required recordsβ
1. SPF (Sender Policy Framework)β
A single TXT record at the root domain, listing every service that is
allowed to send mail on behalf of attunelogic.com. SPF must be one
record β multiple SPF records is itself a misconfiguration that causes
PERMERROR.
Template:
attunelogic.com. TXT "v=spf1 include:<mail-provider-include> ~all"
For example, if mail is sent through SendGrid:
attunelogic.com. TXT "v=spf1 include:sendgrid.net ~all"
If we send through more than one provider (e.g. SendGrid for transactional, Google Workspace for human mail), include both:
attunelogic.com. TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
Use ~all (soft fail) until DMARC reports look clean, then move to -all.
2. DKIM (DomainKeys Identified Mail)β
DKIM must be configured per provider. Each provider exposes one or more selectors and gives you the public key TXT records to add. Naming convention:
<selector>._domainkey.attunelogic.com. TXT "v=DKIM1; k=rsa; p=<base64 public key>"
Provider-specific notes:
- SendGrid: use the SendGrid "Domain Authentication" wizard; it will give you 3 CNAMEs. Use those rather than typing TXT records by hand.
- Postmark: 1 TXT record per server, plus a return-path CNAME.
- AWS SES: 3 CNAMEs from the "Easy DKIM" flow.
3. DMARC (Domain-based Message Authentication)β
A single TXT record at _dmarc.attunelogic.com.
Phase 1 β observation only (recommended first ~2 weeks):
_dmarc.attunelogic.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc@attunelogic.com; ruf=mailto:dmarc@attunelogic.com; fo=1; adkim=s; aspf=s"
This makes mailbox providers send us aggregate reports (rua) without yet
rejecting any mail. Forward dmarc@attunelogic.com to a real human inbox or
a parsing service (Postmark DMARC, dmarcian, Valimail, etc.).
Phase 2 β enforce:
_dmarc.attunelogic.com. TXT "v=DMARC1; p=quarantine; pct=25; rua=mailto:dmarc@attunelogic.com"
Then ratchet pct up to 100 and finally move p=quarantine β p=reject
once aggregate reports show 100% authenticated mail for at least 7 days.
4. MTA-STS and TLS reporting (optional but recommended)β
Add a TXT record and a small static site at
mta-sts.attunelogic.com/.well-known/mta-sts.txt. This is non-blocking for
launch but will be expected by Microsoft 365 within a year.
Verification checklistβ
Before declaring email "configured for production":
- DNS records are live (use
digfrom at least two networks, or https://mxtoolbox.com /dig @8.8.8.8). - Send a test email from the actual production API to https://www.mail-tester.com and confirm a score of 9/10 or better.
- Send a test email to a Gmail account and to an Outlook.com account and
confirm:
- SPF=pass
- DKIM=pass
- DMARC=pass (visible in "Show original" / message source)
- DMARC aggregate reports begin arriving within 24 hours.
- Add a synthetic monitor (Better Uptime, Datadog Synthetics, or a cron that hits the password-reset endpoint) so future regressions are detected automatically.
Rolling backβ
DNS changes propagate within minutes for our TTLs, but recursive resolvers will cache for up to ~1 hour. To roll back:
- Revert the offending DNS record to its prior value.
- Lower the TTL for that record to 300 seconds at the same time.
- Notify support so they can preempt customer "I never got the email" tickets.
Code touchpointsβ
- API mailer entrypoint:
src/services/mailer/index.js - Templates:
src/services/mailer/templates/ - Sender address is currently set per-tenant; ensure the From: domain
always matches the SPF/DKIM-aligned domain (
attunelogic.com) regardless of tenant. If we ever support custom sending domains per tenant, this document must be expanded to cover that flow.
Owner and review cadenceβ
- Owner: Platform / SRE (currently the API maintainer).
- Review every 90 days, or whenever the mail provider changes.