Articles

Email Verification and Deliverability: How It Works and Why Emails End Up in Spam

Learn how email verification flows work, why transactional emails land in spam, and how to configure SPF, DKIM, and DMARC to fix deliverability.

Updated 2026-04-12

#email#deliverability#transactional email#DNS#authentication

What email verification is and why products use it

When you sign up for a web application, you're usually asked to verify your email address before getting full access. This isn't just a formality — it serves several real purposes.

Why apps require email verification:

  • Confirms the email address actually belongs to you
  • Prevents people from signing up with someone else's email
  • Establishes a working communication channel before the account is active
  • Reduces spam accounts and bot registrations
  • Ensures password reset and important notifications can reach the user

The flow is almost always the same: you enter your email, the server generates a unique one-time token, embeds it in a link, sends it to your inbox, and waits for you to click it. When you click the link, the server validates the token, marks the email as verified, and logs you in.

How the email verification flow works step by step

Here is the typical lifecycle of an email verification:

1. User submits signup form

The server receives email and password. It creates a new user account in the database and sets a flag like requiresEmailVerification: true. The account exists but isn't active yet.

2. Server generates a verification token

A cryptographically random token is created — usually 32–64 bytes of randomness, encoded as hex or base64. This token is hashed before storage (so the database never holds the raw value), and given an expiry time, typically 24 hours.

3. Verification email is sent

The server calls an email provider (like Resend, SendGrid, or AWS SES) with a link in the format:

https://yourapp.com/api/auth/verify-email?token=abc123...

The email provider delivers the message to the user's inbox.

4. User clicks the link

The browser hits your server with the token in the URL. The server:

  • Looks up the token (by hashing the incoming value and matching against stored hashes)
  • Checks it hasn't expired
  • Checks it hasn't already been used
  • Marks the token as used
  • Updates the user: emailVerifiedAt = now, requiresEmailVerification = false
  • Creates a session and logs the user in
  • Redirects to the application

5. Token is invalidated

Once used, the token can never be used again. If the user requests another verification email, the old tokens are invalidated and a new one is issued.

One-time tokens are critical. A verification token should only work once. If you click the same link twice, the second click should fail. This prevents replay attacks and ensures each email gets verified exactly once.

Why verification emails land in spam

This is the most common complaint with transactional email. The email was sent successfully (the API returned 200 OK), but it never appeared in the user's inbox — or worse, arrived in the Spam folder.

There are several reasons this happens.

Missing DNS authentication records

This is the most common cause. Email providers and spam filters check whether the sending server is authorized to send email on behalf of the domain. If the DNS records (SPF, DKIM, DMARC) are missing or misconfigured, many filters will reject or quarantine the message.

Sending from a shared or untrusted domain

Many email providers (including Resend) offer a default sending domain for testing — something like onboarding@resend.dev. This domain is shared by thousands of developers, and its reputation is mixed. Emails from shared domains frequently land in spam.

The fix is always to use your own domain.

New domain with no sending history

Email spam filters consider domain age and sending history. A brand new domain that suddenly starts sending thousands of emails looks suspicious. This reputation builds over time and through proper authentication.

Content that triggers spam filters

Certain patterns in email content trigger filters: all-caps subject lines, excessive links, suspicious phrases, HTML with hidden elements, or a very low text-to-image ratio.

No unsubscribe header for marketing emails

For marketing emails, the absence of a List-Unsubscribe header is a strong spam signal. For transactional email (verification, password reset), this is less critical — but for newsletters or promotional content it's mandatory.

Understanding SPF, DKIM, and DMARC

These three DNS-based systems form the foundation of email authentication. Together they tell receiving mail servers: "this message really came from who it claims to be from."

SPF — Sender Policy Framework

SPF is a DNS record that lists which mail servers are allowed to send email on behalf of your domain.

When Gmail receives an email claiming to be from noreply@yourdomain.com, it checks the DNS of yourdomain.com for an SPF record and asks: "Is the server that sent this email on the approved list?"

How to set it up:

Add a TXT record to your domain's DNS at the root (@):

v=spf1 include:amazonses.com ~all

If you're using Resend, the value they give you looks like:

v=spf1 include:_spf.resend.com ~all

The ~all at the end means "soft fail" — emails from unlisted servers will be marked suspicious but not outright rejected. Using -all (hard fail) is stricter and rejects them entirely.

You can only have one SPF record per domain. If you already have one, don't add a second — merge the include: statements into a single record: `` v=spf1 include:_spf.google.com include:_spf.resend.com ~all ``

DKIM — DomainKeys Identified Mail

DKIM adds a cryptographic signature to every outgoing email. The sending server signs the message with a private key, and the receiving server verifies it using a public key published in your DNS.

This proves two things: the email came from your server, and the content wasn't tampered with in transit.

How to set it up:

Your email provider generates a key pair. You publish the public key as a DNS TXT record. The format is:

Name:   resend._domainkey.yourdomain.com
Type:   TXT
Value:  v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4G...

The exact name and value are provided by your email provider in their domain setup flow. You copy them into your DNS exactly as shown.

DKIM records can take up to 48 hours to propagate, though usually it's under an hour.

DMARC — Domain-based Message Authentication, Reporting, and Conformance

DMARC builds on SPF and DKIM by telling receiving servers what to do when those checks fail. It also gives you visibility through aggregate reports sent to your email address.

A basic DMARC record:

Name:   _dmarc.yourdomain.com
Type:   TXT
Value:  v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com

The p= parameter controls the policy:

PolicyMeaning
p=noneMonitor only — don't reject anything, just send reports
p=quarantineSuspicious emails go to spam
p=rejectFailing emails are rejected entirely

Start with p=none while you're getting set up. This lets you receive reports without risking legitimate mail being dropped. Once you've confirmed your SPF and DKIM are working correctly, move to p=quarantine and eventually p=reject.

Setting up a custom sending domain step by step

Here is the full process for configuring a custom domain with a transactional email provider.

Step 1: Add your domain in the provider dashboard

Log in to your email provider (Resend, SendGrid, Postmark, etc.) and navigate to Domains. Add your domain — for example yourdomain.com. The provider will show you a list of DNS records to add.

Step 2: Add the DNS records

Go to your DNS provider (Cloudflare, Vercel, Namecheap, Route 53, etc.) and add each record exactly as shown:

  • One TXT record for SPF (at @ or root)
  • One TXT record for DKIM (at a name like resend._domainkey)
  • One TXT record for DMARC (at _dmarc)

Step 3: Verify in the dashboard

Return to your email provider and click "Verify" or "Check DNS". The provider will query your DNS and confirm each record is present and correct.

DNS changes can take anywhere from a few minutes to 48 hours to propagate globally. If verification fails immediately after adding the records, wait 10–30 minutes and try again.

Step 4: Update your sending address

Change the from address in your application to use your verified domain:

From: PromptPlan <noreply@yourdomain.com>

Never send from onboarding@resend.dev or similar provider default addresses in production.

Step 5: Send a test email

Send a test email to your own inbox and check that it arrives without spam warnings. Check the email headers (most email clients let you view raw headers) to confirm the DKIM signature is present and the SPF check passed.

How to check if your email is configured correctly

Several free tools can analyze your email configuration and tell you exactly what's wrong:

MX Toolbox — Check your SPF, DKIM, and DMARC records without sending anything. Enter your domain at mxtoolbox.com.

Mail Tester — Send a test email to a temporary address they give you, and receive a detailed spam score with line-by-line analysis of what's wrong. Visit mail-tester.com.

Google Postmaster Tools — If you send significant volume to Gmail addresses, Google's Postmaster Tools shows your domain reputation, spam rate, and authentication success rates. Free to set up at postmaster.google.com.

Provider dashboard — Your email provider's dashboard usually shows delivery status for each email: delivered, bounced, or marked as spam. For Resend, check the Emails tab and look at the providerMessageId to trace individual messages.

Common problems and how to fix them

"I added SPF but emails still go to spam"

SPF alone is not enough. Spam filters increasingly require all three: SPF, DKIM, and DMARC. Add DKIM and DMARC as well.

"Verification failed after adding DNS records"

DNS propagation takes time. Wait 15–30 minutes and try again. If it still fails after an hour, double-check the record names and values — a single extra character or wrong subdomain will break it.

"I have two SPF records and verification fails"

Merge them into one. DNS allows only one SPF TXT record at the root. Combine multiple include: statements into a single record.

"Email arrives but the link is expired"

Token expiry is typically 24 hours. If the user waited too long, they need to request a new link. Make sure your verification page has a clear option to resend the email.

"Email arrives in Gmail's Promotions tab"

This is not spam — it's a categorization choice by Gmail. For transactional email, include a clear preheader, avoid promotional language, and make sure the from name is recognizable. Some users may need to drag the email to Primary once, which trains Gmail for future messages.

"The email was 'delivered' according to the provider but never arrived"

The provider marked it delivered when the receiving server accepted it — but the receiving server may have moved it to spam internally. Check the spam folder. If it's consistently missing, run a test through Mail Tester to identify the specific spam trigger.

Summary

Email verification is a standard security pattern: the server sends a one-time link to confirm ownership of the address, and clicking it activates the account. The flow is straightforward — the hard part is making sure the email actually arrives.

To ensure reliable delivery:

  1. Use a custom domain — never send from a shared provider domain in production
  2. Configure SPF to authorize your mail server
  3. Configure DKIM to cryptographically sign your messages
  4. Configure DMARC to set a policy and receive reports
  5. Use a trusted transactional email provider (Resend, Postmark, SendGrid)
  6. Test with Mail Tester and monitor with your provider dashboard

Most spam problems come down to missing DNS records. Once SPF, DKIM, and DMARC are all passing, the vast majority of deliverability issues disappear.

Related reading

PreviousHow Google Finds and Indexes Your Website, and Why It Sometimes Doesn’t
NextHow to Audit Third-Party GitHub Repositories Before You Run Them