@Mail Domain Check

Better Auth - email verification - password reset - OTP

Better Auth verification email not received: trace the callback, sender, and token

Better Auth creates the authentication boundary, but your configured email callback or plugin still owns message delivery. Separate token creation, sender execution, provider handoff, receiver visibility, and final link or OTP use before changing DNS or retrying.

Build the first-action plan locally

Start with seven separate proofs

1. Flow
Classify email verification, reset password, magic link, email OTP, organization invitation, or infrastructure email service. They use different callbacks and terminal states.
2. Auth state
Prove the user category, email verification requirement, sign-up policy, token lifetime, and request timestamp without exposing the address or token.
3. Callback execution
Confirm that sendVerificationEmail, sendResetPassword, sendMagicLink, sendVerificationOTP, or a managed email-service call actually ran.
4. Durable handoff
Do not treat a fire-and-forget callback as delivery. Preserve a provider message ID, queued job ID, or explicit failure class.
5. Provider event
Follow suppressed, failed, delayed, bounced, sent, accepted, or delivered events using the provider's documented semantics.
6. Receiver visibility
Check spam, quarantine, security gateways, forwarding, aliases, rules, storage, and a trusted original header when a controlled message is available.
7. Token use
If the message arrived, separate expiration, duplicate use, scanner prefetch, wrong callback URL, and successful verification from mail delivery.

Build the first-action plan locally. No recipient, message, verification URL, reset link, OTP, token, cookie, API key, SMTP password, provider secret, database row, or wallet secret is entered.

Never paste a verification URL, reset URL, OTP, bearer token, session cookie, provider API key, SMTP credential, database row containing a user address, seed phrase, private key, or wallet signature into a public issue or diagnostic.

Identify the Better Auth flow first

Better Auth supports email and password authentication plus plugins such as magic link and email OTP. A missing reset email is not the same incident as a missing sign-up verification email or an OTP that arrived but failed verification.

  • Email verification uses the emailVerification configuration and its sendVerificationEmail callback. If sendOnSignUp is enabled, prove that the sign-up path reached this callback.
  • Password reset uses emailAndPassword.sendResetPassword. A reset-token URL can be created without proving that the message was sent or accepted.
  • Magic link uses the magic-link plugin and its sendMagicLink function.
  • Email OTP uses the email-OTP plugin for sign-in, verification, or password reset with one-time codes.
  • Better Auth Infrastructure email service is a managed provider path. Its API result and message ID must still be followed to provider and receiver evidence.

Record the flow name, deployment environment, Better Auth version, route, UTC time, random correlation ID, bounded response, and callback name. Keep the address, token, OTP, full URL, cookies, and provider secret private.

Check auth state before the mail path

  1. Confirm that email and password auth is enabled when the flow depends on it. If sign-up is disabled or an account already exists, the expected email can change.
  2. Classify whether email verification is required before session creation, optional after sign-up, or handled by a plugin.
  3. For password resets, capture whether the user lookup produced the neutral anti-enumeration response, a private known-user reset, or an account-state block.
  4. For OTP or magic link, record the purpose, token lifetime, resend limit, and whether a previous token was invalidated.
  5. For invitations or organizations, prove the invitation object, inviter authority, recipient role, and accepted or expired state separately from email transport.

Do not weaken enumeration protection to debug a public report. Use private categories and a controlled test account owned by the operator.

Prove the callback or provider call ran

The most common false success is an authentication endpoint that returns before the mail function is awaited, committed, or observable. A callback that calls void sendEmail(...), a background task, or a queue without durable state can make the UI say "sent" while the provider never accepted the message.

  • Wrap the callback with a correlation ID and bounded status such as queued, provider_accepted, provider_failed, or callback_threw.
  • Persist the attempt after the auth state is valid and before the UI claims that a message was sent.
  • Keep provider message ID, template ID, stream, From identity, and response code. Do not log the URL, token, OTP, recipient, or full provider payload.
  • If the app uses a managed email service, preserve its returned message ID and error class; do not stop at an HTTP 200 or a promise resolution.

Follow the provider event, not the framework label

Once Better Auth or the email-service wrapper hands off the message, the email provider owns the next boundary. Read the provider event trail before changing DNS.

  1. Confirm the verified From domain or Sender identity that actually sent the message.
  2. Check account state, plan or quota, suppression list, template rendering, region, stream, and webhook delivery.
  3. Preserve the first suppression, failure, deferral, bounce, or SMTP reply. Decode the SMTP reply locally before retrying.
  4. If the provider reports delivered, retain the provider's definition. Usually it means a receiving mail system accepted the message, not that the user saw it.

Check the actual visible From domain and Return-Path used by the provider. Public DNS for the wrong app domain cannot explain a missing Better Auth email.

After provider acceptance, inspect the receiver

Scope the incident: one mailbox, one receiving domain, one template, one provider stream, one deployment, or every transactional email. Then inspect spam, junk, quarantine, security gateways, aliases, forwarding, rules, storage, and mailbox trace data.

For a controlled message that arrived, export the complete original header and analyze it locally. Compare trusted receiver timestamps, SPF, DKIM, DMARC alignment, visible From, Return-Path, DKIM d=, and the newest trusted Received fields.

Separate token use from mail delivery

If the user can find the email but the link or code fails, stop treating the problem as deliverability.

  • Correlate token issuance, provider acceptance, receiver acceptance, first URL request, app callback result, and final verification or reset state.
  • Detect link-preview or security-scanner prefetch without logging the secret URL.
  • Keep expiration, duplicate use, replaced token, wrong callback host, wrong environment, and successful completion as separate outcomes.
  • Do not extend token lifetime or make tokens reusable as a delivery workaround.

The first ten minutes

  1. Name the exact Better Auth flow and callback.
  2. Record auth-state categories and a random correlation ID.
  3. Prove callback execution, durable attempt state, and provider message ID or bounded failure.
  4. Follow the provider event trail to suppression, failure, delay, bounce, or receiver acceptance.
  5. Check private receiver evidence only after provider acceptance.
  6. If the message arrived, debug token or OTP use as a separate incident.

Build a safe incident packet

Application
Flow, route, environment, Better Auth version, UTC time, correlation ID, and bounded response.
Auth state
User-state category, verification requirement, token purpose, token lifetime, and resend or invalidation policy.
Callback
Callback name, attempt state, provider name, template or stream alias, message ID, and bounded error class.
Provider
Ordered event names, SMTP reply category, sending IP, visible From domain, and Return-Path domain.
Receiver
Affected receiving domain and scope, quarantine or trace result, and controlled original-header evidence.
Completion
Expired, invalid, prefetched, duplicate, wrong host, wrong environment, or accepted state without the secret.

Inventory each Better Auth email path locally, including flow, provider, visible From domain, DKIM identity, Return-Path, owner, provider event, receiver evidence, and rollback owner.

Check the public sender controls only after you know the visible From domain. DNS cannot read Better Auth callbacks, provider logs, receiver quarantine, OTP state, or a private database.

This independent guide is not affiliated with Better Auth. Better Auth, the configured email provider, and the receiving administrator remain the sources of truth for their private systems.

Primary references