Your app says it sent the email. So where did it actually go?
Transactional email is the one part of your product that fails with every light still green. The API returns success, the dashboard shows no bounce, your logs say sent — and the customer sits on a login screen waiting for a link that was dropped before it ever left the building. Here is why signup, magic-link, and password-reset emails go missing, and the one check that measures what the customer actually gets.
“Sent” and “received” are different systems.
Everything you can see stops at the moment your provider accepts the message. What decides whether a customer reads it happens after that, in systems you don’t own: a suppression list holding an address hostage from a bounce nine months ago, a receiving server weighing an SPF record that a website migration quietly rewrote, a spam filter making a judgement about a domain you only send from twice a week. None of that reports back to your dashboard as a failure — which is exactly why the usual monitoring setup, watching your app’s uptime and your provider’s API, is blind to the single highest-stakes flow you have. A customer who never gets the login link doesn’t file a bug. They leave.
Seven reasons a “delivered” email never arrived
The address is on your provider’s suppression list
Symptom: One specific customer never gets anything — reset links, receipts, alerts — while everyone else is fine. Your send logs look successful.
Fix: Look the address up in your provider’s suppression or bounce list and remove it. A single hard bounce months ago suppresses an address permanently, and the API keeps returning success for every send afterwards.
SPF, DKIM, or DMARC broke in a DNS edit
Symptom: Delivery quietly got worse for everyone after a domain, host, or registrar change — more spam-folder placement, some outright rejections.
Fix: Re-check the TXT records: SPF must still include your sending provider, the DKIM selector record must still resolve, and DMARC must still align. A new marketing tool overwriting the SPF record is a classic cause.
The sending domain was never fully verified
Symptom: Email works to your own address and to colleagues, and fails or lands in spam for everyone outside the company.
Fix: Confirm the domain is verified end to end with the provider, not just the single sender address. Half-verified domains often deliver internally and get filtered externally.
You hit a plan quota or a sending rate limit
Symptom: Email stops at roughly the same time of day or month, then resumes on its own.
Fix: Check the daily and monthly send allowance on your plan and any per-second rate limit. Providers commonly accept the call and drop or defer the message once you are over.
The automation that sends the email is paused
Symptom: Nothing changed in your app, but a specific email type — welcome, receipt, invite — stopped going out entirely.
Fix: If a Zapier, Make, or n8n scenario sends it, confirm it is switched on and still in task credit. Automations pause themselves after repeated errors and rarely tell you loudly.
The template renders empty or the link is broken
Symptom: The email arrives, but the button goes nowhere, the link 404s, or the message body is blank.
Fix: A missing template variable often renders as an empty string rather than an error. Assert on the link inside the delivered message, not just on the message arriving.
The link expired before anyone could use it
Symptom: Customers say the login link is “invalid” or “expired” even though they clicked it right away.
Fix: Compare the token’s lifetime against real delivery latency. A 5-minute magic link is unusable when mail is being greylisted and takes eight minutes to land.
Paste this into your incident notes
Transactional email triage
[ ] Is it ONE address or EVERY address? One = suppression list.
Every = DNS, domain verification, quota, or a paused automation.
[ ] Look the recipient up in the provider's suppression / bounce list
and check whether it was suppressed by an old hard bounce.
[ ] Confirm the send was accepted (message ID) AND what happened after.
[ ] Check SPF still includes the sending provider:
dig +short TXT DOMAIN | grep spf1
[ ] Check the DKIM selector record resolves:
dig +short TXT SELECTOR._domainkey.DOMAIN
[ ] Check DMARC exists and aligns:
dig +short TXT _dmarc.DOMAIN
[ ] Check today's and this month's send allowance on the plan.
[ ] Confirm any Zapier / Make / n8n sender is ON and in credit.
[ ] Open the delivered message and click the link — a blank template
variable renders as an empty href, not an error.
[ ] Compare token lifetime against real delivery latency. A 5-minute
magic link is unusable when mail takes 8 minutes to land.Monitor the round trip, not the send
Nightlamp runs an email_flow check that behaves like a customer: it triggers your real signup, magic-link, or reset endpoint against a mailbox we own, then waits for the message to land and reads it — confirming the subject, the body, and the link inside. If nothing arrives inside the time budget, that is a failure, no matter what your provider’s API said. Alongside it, a dns_record check watches your SPF, DKIM, and DMARC TXT records so a registrar edit can’t quietly break alignment, and an smtp check confirms your mail endpoint is answering with a valid certificate. When one fails, an engineer works out which link in the chain broke and sends you that, not an alert ID.
Find out before the customer gives up
Start a trial and point Nightlamp at your signup or password reset flow. We’ll run it end to end on a schedule against a real mailbox, watch the DNS records that decide whether it lands, and tell you in plain language the moment the loop stops closing.
Start 14-day trial · no cardNewsletter
One real no-code incident + the fix, monthly
A post-mortem from a real no-code outage — what broke, how it was found, and the fix — once a month. Confirming also gets you the no-code incident runbook pack.
Double opt-in. One-click unsubscribe. No spam, ever.