Skip to main content
Episode 14 of 24Payments and email45 min

Send order, verification and password-reset emails

Connect Resend, verify your domain, and send order confirmations, shipping updates, email verification and password reset emails that reach the inbox.

You will have: Four working email types with HTML and plain-text versions, sent from your own verified domain, with abuse limits and a log of what was sent.

Before you start

  • Episodes 11 and 13 are done: accounts and test payments work
  • Own a domain name (for example the one you chose for the store) and be able to log in to where its DNS settings are managed (usually your domain registrar)
  • Create a free Resend account at resend.com
  • Have an email address you can use to receive test emails

Make it yours

Change the example (fashion store) to your own. Your answers are saved in this browser and used in every episode.

Other series keywords (5)

Your prompt

Paste into: Your AI agent opened in your project folder. Keep your Resend API key out of the chat: put it in .env.local when asked.
Context: this project is the online store "Thread & Needle", a fashion e-commerce store selling women's and men's clothing, shoes and accessories in United States. The store's domain is threadandneedle.com. It uses Next.js, TypeScript, PostgreSQL with Drizzle (local database shop_local), Zod and Auth.js. Customer accounts exist (Episode 11) and test payments mark orders paid through a webhook (Episode 13).

Goal: send transactional email (emails triggered by something a customer did) with Resend. Before coding, read Resend's official documentation for its Node SDK and for Next.js, because details change.

Plan first: show me your plan (files, tables, templates, environment variable names) and wait for my OK before changing anything. Explain SPF, DKIM and DMARC in two plain sentences each.

Tasks, after I approve:
1. Walk me through the Resend setup one step at a time: create an API key with the narrowest permission available, add the domain threadandneedle.com (a sending subdomain such as mail.threadandneedle.com is fine), and add the DNS records Resend shows me. Tell me exactly which records to copy and where. Do not guess record values; I will paste what Resend shows. Show me how to check that the domain says Verified. Suggest a basic DMARC record too.
2. Put the key in .env.local only (RESEND_API_KEY, EMAIL_FROM) and add just the names to .env.example. Never print, log or commit the key.
3. Create one small email module with a single send function that always sends both an HTML and a plain-text version, uses a fixed From address on my verified domain, and never throws into the caller: failures are logged without personal data.
4. Build simple, mobile-friendly HTML templates plus plain-text versions for: order confirmation (order number, items, totals, shipping address, link to the order); shipping update (carrier and tracking link, ready to be called from the admin in Episode 16); email address verification; password reset.
5. Send the order confirmation from the payment webhook after an order becomes paid. Use an email_log table with a unique key per order and email type so a repeated webhook never sends a second copy. An email failure must never make the webhook fail.
6. Verification and reset links: use a long random token, store only its hash, set an expiry (reset: short, verification: longer), allow one use, and invalidate old tokens when a new one is made. The reset form must give the same answer whether or not the email exists, so nobody can find out who has an account.
7. Abuse care: rate-limit verification and reset requests per email address and per IP address. Escape every customer-provided value inside templates.
8. Development mode: if there is no API key, print the email to the terminal instead of sending it, but only when NODE_ENV is development.

Security rules: no secrets in code or chat; do not put tokens or full addresses in logs; send nothing marketing-related from these templates.

Verify yourself and show results: Vitest tests for token expiry, one-time use and the duplicate-send guard; then send each of the four emails to my test address and tell me what to look for. Run the type check and linter.

Commit to a branch named feature/transactional-email with a clear message.

Report back in this format: 1) What was done. 2) What I should check in my inbox and the Resend dashboard. 3) Anything that needs my decision.

Review the prompt, then copy it. Never paste real passwords or API keys into a chat.

How to check it worked

  • The Resend dashboard shows your domain as Verified
  • A test order payment produces one order confirmation email, and replaying the webhook does not send a second one
  • The email shows correctly on a phone and the plain-text version is readable
  • A password reset link works once, then stops working, and expires after the time the agent set
  • Asking for a reset for an email that has no account shows the same message as for a real one
  • The email_log table lists what was sent and the sent emails do not land in spam (check the spam folder once)

Sample result

Example: fashion store

Example order confirmation for the fashion store, and the agent's send log.

If something goes wrong

The domain stays on Pending in Resend.
DNS changes can take minutes to hours. Check that each record was copied exactly (no doubled domain name, correct type), that you added them at the place that really controls your DNS, then use Resend's verify button again.
Emails arrive in spam.
Confirm SPF and DKIM both show Verified, send from the verified domain (not a free mailbox address), keep the subject plain, and keep the plain-text version. Ask the agent to review the templates for spammy wording.
Resend refuses to send, or only sends to your own address.
Testing without a verified domain is limited to your own account address. Finish domain verification first, then change EMAIL_FROM to an address on that domain. Also check that the key was created with sending permission.
The reset link says expired or invalid straight away.
Ask the agent to check the token lifetime, the system clock handling and that the link contains the full token. Request a new link, because every new request cancels the previous one.

Resend has a free plan with a daily sending limit and a monthly limit. Verify the current numbers, and whether more than one domain is allowed, on Resend's pricing page before you rely on them.

More in Payments and email

This is part of the series Build a secure, no-cost e-commerce website with AI agents. See all 24 episodes, the tools used and answers to common questions on the series page.

We use optional cookies for analytics and referral tracking. Learn more about our privacy policy