Troubleshooting: email DNS verification drift and recovery
DNS verification is not a one-time check. The platform re-validates the SPF, DKIM, and DMARC records behind a sending domain on a daily health check, and every interactive re-check folds into the same verdict. When a record drifts — a TTL expires into a changed answer, someone edits the zone at your registrar, or a key rotates — the domain regresses: the traffic light goes yellow or red, a bell notification flags the regression, and sends from that domain start failing at the provider with a 403 “domain is not verified” rejection. This page is the recovery procedure. If the bounce or complaint pages described your symptom instead (recipient-side rejects after the provider accepted the send), work the email bounces and spam complaints page — this page covers only the sender-side DNS-verification failure class.Where you see the drift
- Email dashboard — the DNS status widget shows one traffic-light chip per record type (SPF, DKIM, DMARC) for the platform-default domain and your custom domain, updated about once a minute. Green means the record resolves to the expected value; yellow is a partial or inconclusive verdict; red is a confirmed failure.
- Send path — a send through a regressed domain fails with a provider 403: “domain is not verified.” The Delivery Log row stays on your side of the provider handoff.
- Bell notification — when the daily re-check or an interactive re-check sees a previously-green record flip to a confirmed failure, the platform writes the regression to the domain’s history and raises a notification. A resolver blip (SERVFAIL, timeout) never fires the bell — only confirmed failures become regressions.
Step 1 — Read the traffic light
GET /api/v1/email/domains/:domainId/dns-status returns the per-record
verdict for one sending domain. Requires an owner or admin role.
data.overallStatusis the worst of the per-record verdicts:valid(green, all records pass),warning(yellow, partial or inconclusive),invalid(red, at least one confirmed failure), orunknown(not checked yet).data.records[]carries one entry per record withkind(spf/dkim/dmarc, plusmxandbimiwhere configured),status, the DNS value the platform saw (recordActual), and the value it expected (recordExpected) — compare the two to find the drift.- For DKIM, each selector appears as its own entry with a
selectorfield; a single failing selector turns the whole domain red. data.providerSendability(present when the provider was consulted on this read) tells you whether the email provider actually considers the domain registered. An all-green DNS grid with a “not registered” provider verdict still 403s — that case is Step 3 below.- Pass
?refresh=trueto force a live re-check instead of the cached verdict; the interactive Verify DNS / Re-check now action in the dashboard uses the same flag and also pokes the provider to re-verify.
Diagnose per record
Every red or yellow record maps to one fix:
Two honest noise cases to rule out before you edit anything:
- Yellow with an empty actual value usually means DNS has not propagated yet. A newly published record caches at the resolver for its TTL; wait it out and re-poll rather than re-editing.
- A status that flips red for minutes then recovers on its own is a resolver blip, not drift — those verdicts are deliberately excluded from the regression bell and history trend, and need no action.
Step 2 — Check the history before you edit
GET /api/v1/email/domains/:domainId/dns-history?days=30 returns
timestamped snapshots of the record verdicts over the window (default 30
days, up to 90). Use it to answer two questions:
- When did it regress? The first red snapshot after a green stretch lines up with a zone edit on your side — match the timestamp against your registrar change log before you revert the wrong record.
- Did it recover? After a fix, this log is how you confirm the verdict flipped back once the TTL expired; a green live status with a still-red recent history means the fix published but the cached verdict has not rolled yet.
Step 3 — Re-verify on demand
The daily health check catches drift eventually, but you never need to wait for it. Run an interactive re-check after every DNS edit:Re-running verification against an unchanged, still-broken zone is a
no-op loop. Confirm
dns-status is green (or at minimum that
recordActual now matches recordExpected) before you expect the 403
to clear.Step 4 — Watch the warm-up posture while the domain is down
While a domain is regressed and refusing sends, two things change under you:- Any dedicated-IP warm-up loses its anchor — the ramp is anchored to the
first recorded send, and suppressed sends do not advance it. Read
GET /api/v1/email/warmup-statusafter the regression clears: it tells you today’s recommended cap, how much headroom is left today, and whether the reputation feedback loop is holding your ceiling down. - If you queued a campaign into a regressed domain, those rows sat in the queue rather than bouncing — drain them after re-verification instead of re-sending duplicates (see message stuck in queued for the queue-side diagnosis).
GET /api/v1/email/warmup-enforcement answers allow / partial /
block for the batch size — a block there is the ramp doing its job,
not a new fault.
Step 5 — Resume sending
A domain is back to fully operational when all three hold:GET /api/v1/email/domains/:domainId/dns-statusreturnsoverallStatus: "valid"with no red or yellow record.providerSendabilityon the same response reports the domain as registered with the provider (or the interactive verify already ran end-to-end).GET /api/v1/email/domains/:domainId/dns-historyshows the recent snapshots green past the record TTL.
delivered in the Delivery Log), then resume the paused campaign or
queued traffic. The regression bell clears on its own once the daily
health check sees green again.
Recovery checklist
- Read
dns-statusfor the domain; note whichkindis red or yellow. - Match
recordActualagainstrecordExpectedfor that record; the mismatch is the drift. - Check
dns-historyfor the regression timestamp; line it up with your registrar change log. - Fix the record at your registrar (single SPF, full DKIM
p=key, DMARC policy in place, Return-Path CNAME intact). - Run
POST /api/v1/settings/channels/email/verify-dns(or the dashboard Verify DNS button) and confirm the provider verdict. - Re-poll
dns-status; confirm green, then confirmdns-historyrolled past the TTL window. - Check
warmup-statusbefore resuming volume past the day’s cap. - Resume queued congestion control (not bulk re-sends — drain the queue).
When to escalate
Escalate when the record you published is demonstrably correct but the verdict never moves, or when the provider disagreement will not clear:- The DNS zone publicly resolves the exact expected value (
dig TXT <name>matchesrecordExpected), yetdns-statuskeeps reporting yellow or red past a full TTL cycle. providerSendabilitystill reports “not registered” after an interactive verify on an all-green grid.- Every record reads
unknown— that points at the platform’s check path, not your zone.
- Your tenant ID (Settings → Organization in the dashboard; also
returned as
organizationIdbyGET /api/v1/me). - The sending domain affected.
- The record kind and the expected/actual values the
dns-statusresponse returned for the failing record. - The timestamp of the first red snapshot from
dns-history.
See also
- Email channel guide — domain setup, the verification lifecycle, and webhook events
- Email bounces and spam complaints — the recipient-side failure class that survives a green DNS grid
- Message stuck in queued — drain queued email rows without re-sending duplicates
- Email endpoints reference — request
and response shapes for
dns-status,dns-history, andwarmup-status