Dashboard

How to Add Email Verification to an AI-Built App

A working email verification flow needs more than a link and a token. The security details and product decisions that are easy to skip.

Steve Jefferson
Steve Jefferson
Developer Advocate
16 September 20261 min read

How to Add Email Verification to an AI-Built App

Adding email verification to an AI-built app means generating a single-use, time-limited token at signup, emailing a link containing that token, and marking the account verified only when the token comes back matched and unexpired, then deciding deliberately how much the app lets an unverified user do in the meantime.

The core flow

  1. On signup, create the user record with `email_verified = false`.

  2. Generate a cryptographically random token (not a guessable sequence, not the user's own ID), store it with an expiry, commonly 24 to 72 hours out.

  3. Email a link containing the token: `https://yourapp.com/verify?token=abc123...`

  4. When the link is visited, look up the token, check it has not expired and has not already been used, then set `email_verified = true` and mark the token consumed so it cannot be replayed.

  5. Provide a "resend verification email" action, since links expire and emails get lost, and this is the single most common support request a missing one generates.

sql
create table email_verification_tokens (
  id uuid primary key default gen_random_uuid(),
  user_id uuid not null references users(id) on delete cascade,
  token text not null unique,
  expires_at timestamptz not null,
  used_at timestamptz,
  created_at timestamptz default now()
);
create index on email_verification_tokens (token);

Storing `used_at` rather than deleting the row on use matters for support and abuse investigation later, a token that was used once and someone is trying again is a different situation from a token that never worked, and you cannot tell the difference if the row is already gone.

The decision that actually matters: what can an unverified user do?

This is the part most AI-generated implementations skip past, defaulting to one of two extremes without your ever deciding which one you wanted:

  • **Block everything until verified.** Safest, but adds friction between signup and any value the user gets from your app, and some real fraction of users never complete verification and simply churn.

  • **Let them use the app fully, verify later.** Best conversion, worst for abuse: an unverified email is not confirmed to belong to the person who signed up, so this approach is risky for anything involving other people's data, payments, or sending content on the user's behalf.

The right default for most apps is in between: let a new user explore and get real value immediately, but gate the specific actions that need a confirmed identity, sending invitations to other people, making a payment, or changing account recovery email, behind verification. State this explicitly in your prompt to an AI coding agent, since left unspecified it will pick one of the two extremes and you likely will not notice which until it matters.

text
New users can sign up and use the app immediately without verifying
their email. Gate these specific actions behind email verification:
sending invites to teammates, any payment action, changing the
account's recovery email. Show a persistent but dismissible banner
prompting unverified users to verify, do not block the rest of the app.

Token security details that are easy to get wrong

  • **Use a real random token**, at least 32 bytes from a cryptographically secure generator, not a UUID derived from predictable data and not a short numeric code unless you are pairing it with rate limiting (numeric codes are fine for a 6-digit code entered manually, not for a link, since links get logged in browser history, proxies, and email scanning services).

  • **Expire tokens**, always. A verification link that works forever is a credential with no lifecycle, sitting in an old email indefinitely.

  • **Invalidate old tokens when issuing a new one.** If a user hits "resend," the previous token should stop working, otherwise you have several simultaneously valid tokens for the same account with no clean way to reason about which ones are still live.

  • **Rate-limit the resend action.** Without a limit, "resend verification email" becomes a mechanism for spamming an arbitrary email address with your app's messages, since the endpoint typically does not require authentication beyond knowing the email itself.

How this fits with the rest of your auth setup

Email verification is one piece of an account security stack, not the whole thing. If you are also adding two-factor authentication, verify email first in the user's lifecycle, since 2FA recovery flows commonly depend on a confirmed email address to send a reset link to. If your app supports social login, note that most providers hand you an already-verified email, in which case skip your own verification step for that signup path rather than making a user verify an address the provider already confirmed. And if SSO login is in scope for enterprise customers, email verification for those users is typically irrelevant, since the identity provider is the trust source, not your app's own token flow.

Frequently asked questions

24 to 72 hours is standard, long enough to survive someone signing up before bed and verifying the next morning, short enough that a stale, unused token is not a long-lived credential. Shorten this for higher-risk apps, extend it if your users have shown a pattern of delayed verification and you would rather keep a slightly longer window than lose them to expiry.

Yes, and it is often a better user experience on mobile, no app-switching to click a link. Pair a numeric code with strict rate limiting and a short expiry (10 to 15 minutes), since a 6-digit space is small enough to brute-force without those protections.

What happens if a user never verifies their email?

Decide this explicitly rather than letting it default to nothing. Common approaches: a periodic reminder banner, restricting gated actions indefinitely until verified, or purging genuinely abandoned unverified accounts after a defined period (30 to 90 days is common) to keep your user table clean.

Should the verification email come from a no-reply address?

Avoid a hard no-reply address if you can. A monitored address, even one you only check occasionally, catches confused users replying with "this isn't working," which is real product feedback about your verification flow that a no-reply address simply discards.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.