dom4in.net
← All resources
Guide · Resend

Send transactional email with Resend

Verify a subdomain with three DNS records, and sending becomes one clean API call — the developer-favorite email API of the last few years, minus SMTP archaeology.

The process

The whole path from signup to production sends that land in inboxes. Use a subdomain, watch the region, and wire the bounce webhook — that's the whole game.

Directions

1Add a sending subdomain

Create an account at resend.com, go to Domains, and add a dedicated sending subdomain like send.example.com rather than the root — it isolates Resend's SPF/DKIM from your mailbox provider's and keeps transactional reputation separate from corporate mail.

Pick the region where mail should be sent from at add time. The visible From address can still be anything at that subdomain: [email protected], or configure the root later once you trust the setup.

2Add the SPF and DKIM records

Resend shows a short list of records: an MX and TXT for the bounce path (SPF) plus a DKIM TXT — exact hosts and values generated per domain, so copy from your dashboard. Add them at your DNS host; on Cloudflare, DNS only as always.

Verification usually lands within minutes. While you're in DNS, confirm the root domain has a DMARC record — Resend's alignment on your subdomain plays fine with a relaxed policy, and inbox providers in 2026 increasingly expect DMARC to exist at all.

send.example.com          MX 10  feedback-smtp.<region>.amazonses.com
send.example.com          TXT    v=spf1 include:amazonses.com ~all
resend._domainkey.send... TXT    p=MIGfMA0GCSq... (your key)

3Create a scoped API key

Create an API key scoped to sending only, and if the dashboard offers domain scoping, pin it to your verified domain. It goes in your backend's secret store — a Workers secret, GitHub Actions secret, or env var on the server. It never ships to a browser.

4Send your first email from code

Sending is a single POST — SDKs exist for every mainstream language, and the raw HTTP call is simple enough to skip them. Send a real test to a mailbox you control and read the headers: spf=pass, dkim=pass with your subdomain, dmarc=pass.

For anything beyond one-off strings, keep templates in your codebase (React Email pairs naturally here) so email markup gets reviewed and versioned like the rest of the app.

await resend.emails.send({
  from: "Acme <[email protected]>",
  to: ["[email protected]"],
  subject: "Your receipt",
  html: "<p>Thanks for your order.</p>",
});

5Handle bounces and complaints

Add a webhook endpoint for email.bounced and email.complained events, verify the signature, and mark those addresses so your app stops sending to them. Repeatedly mailing a hard-bounced address is the fastest way to burn the domain reputation you just built.

The delivered and opened events feed nice product touches too — but the bounce handler is the one that protects deliverability.

Common issues & fixes

Domain stuck unverified.

One record has a doubled domain suffix or the wrong host — compare each against the dashboard character by character, and confirm none are proxied.

403 sending from an address you own.

The From domain must be the verified one. [email protected] fails when only send.example.com is verified — send from the subdomain or verify the root too.

Mail delivers but lands in spam.

New subdomain, no reputation, or no DMARC on the root. Publish DMARC, warm volume gradually, and keep content honest (no link-shortener soup).

Webhook events aren't arriving.

The endpoint must return 2xx fast and be reachable from the internet. Check the signing secret matches and look at delivery attempts in the dashboard.

Hitting rate limits on batch sends.

Transactional APIs assume event-driven sends. Use the batch endpoint where offered, add client-side throttling, and ask support for higher limits with real volume numbers.

Honesty note: articles are drafted with AI assistance, then a human checks each one against the vendor's current setup flow before it publishes — nothing goes live unreviewed. Vendor interfaces still change; if a step looks different, the underlying record is what matters.