Skip to main content

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.
Reading the response:
  • data.overallStatus is the worst of the per-record verdicts: valid (green, all records pass), warning (yellow, partial or inconclusive), invalid (red, at least one confirmed failure), or unknown (not checked yet).
  • data.records[] carries one entry per record with kind (spf / dkim / dmarc, plus mx and bimi where 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 selector field; 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=true to 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:
  1. 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.
  2. 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:
The same Verify DNS / Re-check now button lives under Channels > Email > Domains and on the per-domain page. This does three things in one call: re-resolves every record live, re-checks the domain with the email provider (this is what clears the 403 when the provider never registered an otherwise-green domain), and stamps the verified state so the send path accepts the domain again.
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-status after 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).
If your send burns through the post-recovery ramp cap the same day, 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:
  1. GET /api/v1/email/domains/:domainId/dns-status returns overallStatus: "valid" with no red or yellow record.
  2. providerSendability on the same response reports the domain as registered with the provider (or the interactive verify already ran end-to-end).
  3. GET /api/v1/email/domains/:domainId/dns-history shows the recent snapshots green past the record TTL.
Send one test message through the domain, confirm it lands (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

  1. Read dns-status for the domain; note which kind is red or yellow.
  2. Match recordActual against recordExpected for that record; the mismatch is the drift.
  3. Check dns-history for the regression timestamp; line it up with your registrar change log.
  4. Fix the record at your registrar (single SPF, full DKIM p= key, DMARC policy in place, Return-Path CNAME intact).
  5. Run POST /api/v1/settings/channels/email/verify-dns (or the dashboard Verify DNS button) and confirm the provider verdict.
  6. Re-poll dns-status; confirm green, then confirm dns-history rolled past the TTL window.
  7. Check warmup-status before resuming volume past the day’s cap.
  8. 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> matches recordExpected), yet dns-status keeps reporting yellow or red past a full TTL cycle.
  • providerSendability still 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.
Include these so support traces the domain without a back-and-forth:
  • Your tenant ID (Settings → Organization in the dashboard; also returned as organizationId by GET /api/v1/me).
  • The sending domain affected.
  • The record kind and the expected/actual values the dns-status response returned for the failing record.
  • The timestamp of the first red snapshot from dns-history.

See also