Transactional email Resend setup is straightforward when you treat email as a production system rather than a single API call. This guide walks through the complete path: authenticate a domain, send a message from your backend, prevent duplicates, process delivery events, and diagnose failures.

What “transactional email Resend” means

Transactional email is message delivery triggered by an individual action or system event. A user requests a password reset, completes a purchase, receives an invoice, confirms an email address, gets invited to a workspace, or needs a security alert. The message is specific to that person and that event.

Resend is an email API platform aimed at developers. It supports transactional messages through an HTTP API and SMTP, plus domain management, webhooks, logs, suppression handling, and SDKs for several programming languages. The core send request accepts a sender, recipient list, subject, and HTML or plain-text content.

The important distinction is operational:

  • Transactional email must be timely, accurate, and triggered by an application event.
  • Marketing email is generally a campaign sent to a contact list, often with subscription and unsubscribe workflows.
  • A successful API response means Resend accepted your request; it does not, by itself, prove that the recipient’s mailbox accepted or displayed the message.

For transactional email, your application should model the entire lifecycle. Create the business event, build a message with a stable identifier, submit it once, record the returned email ID, and then consume delivery or failure events. That design avoids the common mistake of treating send() as the end of the process.

When Resend is a good fit for transactional email

Resend is a sensible option when your product needs programmatic email and your team wants a developer-oriented integration. You can use its API directly in an application backend, connect existing software through SMTP, or use webhooks to synchronize email outcomes with your database.

Common use cases include:

  • Account verification and magic-link login emails.
  • Password resets and multi-factor authentication alerts.
  • Order confirmations, receipts, invoices, and shipping notifications.
  • Workspace invitations, comments, mentions, and product notifications.
  • Error alerts, scheduled reports, and internal operational messages.

Choose the HTTP API when you control the application code and want structured responses, tags, idempotency support, and predictable integration patterns. Choose SMTP when an existing application, CMS, plugin, or framework already knows how to send mail with SMTP and changing its mail transport is easier than rewriting it around an API.

Resend’s SMTP endpoint is smtp.resend.com. Its documented SMTP configuration uses the username resend and an API key as the password. It supports implicit TLS on ports 465 and 2465, and STARTTLS on ports 25, 587, and 2587. Use the port and TLS mode your application explicitly supports; do not assume every library treats “SSL,” “TLS,” and “STARTTLS” as interchangeable settings.

If you are comparing providers rather than implementing immediately, look beyond the send-call syntax. Evaluate domain authentication, event webhooks, suppression behavior, observability, retry safety, regional or compliance needs, support expectations, and your projected transactional email pricing. The cheapest per-message plan is not necessarily the least expensive operational choice if duplicate notifications, unexplained bounces, or weak event data create support work.

The production architecture: what you need before sending

A dependable transactional email system has five parts:

  1. An owned sending domain or subdomain. Your application needs a sender address under a domain you control.
  2. DNS authentication. Publish the DNS records Resend generates for your domain and verify them.
  3. A server-side credential. Keep the Resend API key in server-side environment variables or a secret manager.
  4. A message creation path. Generate email content only after the underlying business event is committed or otherwise safe to act on.
  5. An event-processing path. Receive and persist delivery, bounce, complaint, and suppression events through authenticated webhooks.

This architecture matters because transactional email is often tied to critical user flows. A reset link that arrives twice can be confusing; an invoice that never arrives creates support tickets; a hard-bounced address should not keep receiving repeated attempts.

Use a dedicated sending subdomain

Resend recommends sending from a subdomain, such as updates.example.com, rather than the root domain. For transactional email, a practical convention is:

  • notify.example.com for product notifications.
  • account.example.com for verification and security messages.
  • receipts.example.com for payment and order receipts.
  • marketing.example.com for promotional campaigns, if you send them.

A subdomain creates a clearer boundary between message streams and can help isolate sender reputation by purpose. It also makes it easier to understand what a recipient is seeing. For example, Acme Security <security@account.example.com> is easier to audit than a generic sender used for every type of mail.

This is not a reason to create a new domain for every message type. Too much fragmentation makes DNS, DMARC reporting, and operational ownership harder. Use a small, intentional set of sending subdomains and document their purpose.

Do not put the API key in browser code

A Resend API key belongs on your server, worker, queue consumer, or secure automation environment. Never expose it in client-side JavaScript, a mobile app binary, a public repository, screenshots, or email templates.

For production, create a key with Sending access when your service only needs to send email. Resend also supports restricting a sending-access key to a specific domain, which limits the damage if a credential is exposed. Store the key in an environment variable such as RESEND_API_KEY, and give each environment its own key: development, staging, and production should not share one secret.

Keys do not automatically expire, so rotation should be an explicit operational task. Create a replacement key, deploy it everywhere that uses the old one, confirm the new key appears in logs, then revoke the old key. Do not rotate by deleting the old key first unless you are intentionally handling an outage.

Verify a domain before sending transactional email with Resend

The most important setup step is domain verification. Resend requires a verified domain to send to recipients other than your own test address. In the Resend dashboard, add the domain or subdomain, then copy the generated DNS records into the DNS provider that hosts the zone.

Resend shows the specific records for your domain. Do not copy DNS values from a blog post, another account, or an example with example.com; values such as DKIM selectors are generated for the particular setup. The DNS provider interface may ask for a record type, name or host, value or target, TTL, and—in the case of MX records—priority.

SPF, DKIM, and DMARC in plain language

Three acronyms matter in nearly every production email setup:

  • SPF is a DNS TXT policy that identifies systems allowed to send mail for a domain.
  • DKIM cryptographically signs mail so recipients can validate that it was authorized and was not altered after signing.
  • DMARC tells receiving systems how to handle messages that fail authentication and lets domain owners receive authentication reports.

Resend’s domain setup provides the SPF and DKIM records needed for its sending configuration. Add a DMARC record separately after the sending domain is verified. A cautious starting record for a domain that is still being observed is commonly shaped like this:

_dmarc.account.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com"

The exact reporting mailbox and policy are your decisions. p=none asks receivers to deliver normally while producing reports; it is useful while you confirm every legitimate sender is authenticated. A stronger policy such as p=quarantine or p=reject can protect against spoofing, but moving there without understanding all of your legitimate sending sources can disrupt mail. DMARC applies to the domain where the record is published, so decide deliberately whether you are publishing it for the organizational domain, a sending subdomain, or both.

Google’s sender guidelines require all senders to personal Gmail accounts to use SPF or DKIM. Senders that exceed 5,000 messages per day to personal Gmail accounts must use SPF, DKIM, and DMARC; that classification is based on the primary domain and is not limited to one subdomain. Treat authentication as a baseline even if you send well below that threshold.

DNS mistakes that block verification

Most verification problems are configuration errors, not a Resend outage. Check these first:

  1. The record was created at the wrong hostname. DNS dashboards vary: some append the root domain automatically, while others require the full host value.
  2. A record value was edited or wrapped incorrectly. Copy the value exactly, including long DKIM data.
  3. An existing SPF record was replaced. A domain should have one SPF TXT record per hostname. If another provider already sends mail for that hostname, combine authorized mechanisms into one valid record rather than adding a second v=spf1 record.
  4. A proxy or DNS feature altered the record. Mail authentication records must be published as DNS records, not routed through a web proxy.
  5. DNS has not propagated everywhere. Resend notes that global DNS propagation can occasionally take up to 72 hours.

In Resend, a domain can be pending, verified, partially verified, failed, or temporarily failed. A temporary failure is particularly worth investigating: a domain that was working may no longer have publicly visible required DNS records. Do not ignore it simply because earlier sends succeeded.

Send your first Resend transactional email through the API

The cleanest implementation is to send from backend code after an application event has succeeded. The following example uses Node.js and the official resend package. It sends an order receipt after your application has already created an order record.

First, install the SDK:

npm install resend

Set the API key in your server environment:

RESEND_API_KEY=re_your_real_secret

Then create a server-only module, such as lib/email.ts:

import { Resend } from 'resend';

export const resend = new Resend(process.env.RESEND_API_KEY);

Now send a receipt from a route handler, server action, worker, or queue consumer. This example uses a deliberately stable idempotency key based on the order ID:

import { resend } from './lib/email';

type SendReceiptInput = {
  orderId: string;
  customerEmail: string;
  customerName: string;
  total: string;
};

export async function sendReceipt(input: SendReceiptInput) {
  const { data, error } = await resend.emails.send(
    {
      from: 'Acme Receipts <receipts@receipts.example.com>',
      to: [input.customerEmail],
      subject: `Your Acme receipt for order ${input.orderId}`,
      html: `
        <h1>Thanks for your order, ${escapeHtml(input.customerName)}.</h1>
        <p>Order: <strong>${escapeHtml(input.orderId)}</strong></p>
        <p>Total paid: <strong>${escapeHtml(input.total)}</strong></p>
        <p>Keep this email for your records.</p>
      `,
      text: `Thanks for your order, ${input.customerName}. Order ${input.orderId}. Total paid: ${input.total}.`,
      tags: [
        { name: 'type', value: 'receipt' },
        { name: 'order_id', value: input.orderId }
      ]
    },
    {
      idempotencyKey: `receipt/${input.orderId}`
    }
  );

  if (error) {
    throw new Error(`Resend rejected receipt for ${input.orderId}: ${error.message}`);
  }

  return data.id;
}

function escapeHtml(value: string) {
  return value.replace(/[&<>'"]/g, (character) => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    "'": '&#39;',
    '"': '&quot;'
  }[character] as string));
}

Replace receipts.example.com with the verified subdomain in your own Resend account. The from address must use a domain you have verified. Include both html and text: HTML gives you layout and branding, while a plain-text alternative provides a usable fallback for text-focused mail clients and recipients.

The official API uses POST https://api.resend.com/emails. If you integrate without an SDK, authenticate using Authorization: Bearer re_...; Resend’s API documentation also states that API requests require a User-Agent header. For most application teams, the SDK is less error-prone and makes it easier to keep request construction consistent.

For implementation details beyond this example, use the platform’s email API reference and setup guides alongside your application framework documentation.

Prevent duplicate emails with idempotency keys

Duplicate transactional messages are one of the most damaging “it mostly works” failures. They happen when a network timeout occurs after the provider accepted the first request, a background job retries, a queue delivers a task more than once, or two application processes race to handle the same event.

Do not solve this by assuming a failed HTTP response means nothing was sent. A timeout can leave your application uncertain whether the provider received the request. Instead, give each logical email a stable idempotency key.

Resend supports idempotency keys for POST /emails and POST /emails/batch. It checks whether an email with the same key was already sent in the previous 24 hours. Repeating the same request inside that window returns the same result without sending another message. Keys can be up to 256 characters.

Use a key that represents the exact email event:

verification/8f1f7fda-9bde-4a3f-99fa-0d3737c6c0c1
password-reset/user_123/2026-06-12T10:15:00Z
receipt/order_987654
workspace-invite/invite_456

Avoid weak keys such as welcome-email, which would block legitimate emails to different users. Also avoid random keys generated on every retry; random keys defeat deduplication because each retry appears to be a new message.

Idempotency is not a complete database strategy. It only protects Resend sends for the documented 24-hour window. Your own database should still have a durable record such as email_jobs or notification_deliveries, with fields for the business event ID, recipient, template version, Resend email ID, submission status, and timestamps. A unique constraint on the business-event-and-message-type combination is an additional guardrail.

Use webhooks to learn what happened after sending

A send response gives you an email ID. Webhooks tell your application what happened afterward. Resend delivers webhooks as HTTPS requests with JSON payloads for events including sends, deliveries, bounces, complaints, opens, clicks, suppression changes, and more.

For critical transactional mail, start by subscribing to these events:

  • email.delivered to record that Resend delivered the email to the recipient’s mail server.
  • email.bounced to record a permanent recipient-server rejection.
  • email.complained where complaint feedback is available.
  • suppression.added to maintain a local do-not-send state.
  • email.delivery_delayed if you want visibility into temporary delivery trouble.

Do not interpret email.delivered as proof a human read the email. It indicates delivery to the recipient’s mail server. Opens are also not a reliable measure of human attention because privacy features and image loading behavior can distort them. For account verification or password reset, the meaningful product signal is the user completing the action associated with the email link.

Verify webhook signatures and handle duplicates

Your webhook URL is an internet-facing endpoint. Never trust an incoming JSON body merely because it has an event-like shape. Resend signs webhook requests, and its documentation provides verification through the signing secret. Verification must use the raw request body; parsing JSON and serializing it again can change bytes and break signature checks.

Webhook deliveries can also be retried, so make the handler idempotent. Store the unique webhook delivery ID—Resend’s tooling refers to the svix-id header for this purpose—and ignore an event you have already processed. Your database table might look like this conceptually:

webhook_events
- provider_event_id   UNIQUE
- event_type
- resend_email_id
- payload_json
- received_at
- processed_at
- processing_error

A robust handler follows this order:

  1. Read the raw body and signature headers.
  2. Verify the signature before parsing or acting on the event.
  3. Insert the provider event ID with a uniqueness constraint.
  4. Return a successful HTTP response promptly after durable storage.
  5. Process business side effects in a worker or transaction-safe code path.

This prevents a retry from issuing duplicate refunds, repeatedly flagging an account, or writing conflicting message states. Resend also supports replaying webhook events, which is helpful after you fix a handler or recover from downtime. That makes idempotent processing essential, not optional.

Handle bounces, complaints, and suppressions correctly

A bounce is a delivery failure returned by the recipient’s mail server. Some failures are permanent, such as an unknown recipient address; others are temporary, such as a full mailbox or a transient receiving-server problem.

Resend identifies permanent recipient rejections through email.bounced. It also maintains a team-wide suppression list. Addresses enter suppressions after a hard bounce or spam complaint, or when your team manually adds them. A suppressed address is skipped across domains and subdomains in the team rather than being sent another message.

Your application should mirror the intent of suppression even if Resend already blocks future sends:

  • Mark permanently bounced addresses as undeliverable in your own customer data.
  • Stop nonessential notifications immediately.
  • Ask the user for a corrected address through an authenticated in-product flow.
  • Do not automatically remove a suppression solely because a user tries the same address again.
  • Keep an audit trail when a support agent or verified user changes a delivery address.

Be cautious with complaint data. Not every mailbox provider supplies complaint feedback, and Resend specifically notes that Gmail/Google Workspace does not return complained events to its suppression system. That means “no complaint webhook” is not evidence that recipients are happy. Monitor authentication, bounces, support tickets, recipient behavior, and mailbox-provider tools where available.

Before adding a recipient to a high-value workflow, validate input at the point of collection. Syntax validation is useful but cannot prove a mailbox exists or that its owner wants messages. For signup forms, invitation flows, and sales-assisted onboarding, an email address verification tool can be a useful additional check; for existing customers, an explicit verification link remains the strongest proof that the person can receive mail at the address.

Test the full lifecycle without harming deliverability

Testing only a happy-path inbox delivery is not enough. Your system needs to handle a delivery, hard bounce, spam complaint simulation, and suppression behavior before real users encounter them.

Resend provides dedicated resend.dev test recipients that simulate outcomes without requiring fake addresses. Examples include:

  • delivered@resend.dev for a delivery event.
  • bounced@resend.dev for a simulated SMTP 550 5.1.1 unknown-user response.
  • complained@resend.dev for a marked-as-spam scenario.
  • suppressed@resend.dev for suppressed behavior.

You can add labels with plus addressing, such as delivered+signup@resend.dev, to distinguish runs. These test sends count against the account sending quota, so use them deliberately in automated test environments.

A useful acceptance test for the receipt example looks like this:

  1. Create a test order with a known order ID.
  2. Trigger the receipt job using delivered+receipt-order-987@resend.dev.
  3. Confirm your application stored one logical receipt job and one Resend email ID.
  4. Confirm an email.delivered webhook was verified and persisted.
  5. Retry the exact job with the same idempotency key.
  6. Confirm the API result maps to the original email and no second logical delivery is created.
  7. Run the same flow with bounced+receipt-order-987@resend.dev.
  8. Confirm the address or test recipient state is marked as undeliverable and no automatic loop resends the message.

This is how you tell the setup worked: not merely by seeing an email in your personal inbox, but by proving your system behaves correctly in both success and failure paths.

Deliverability practices that matter for transactional mail

Provider choice does not override recipient expectations. Authentication, relevance, consistency, and recipient-list quality affect whether a message reaches the inbox.

Start with these practical rules:

  • Send from a verified domain that matches your product identity.
  • Keep the visible From name stable and recognizable.
  • Make subjects explicit: “Reset your Acme password” is clearer than “Important account update.”
  • Send only messages the recipient triggered, requested, or reasonably expects.
  • Keep password-reset and verification links short-lived in your own application logic.
  • Include a plain-text part and avoid overly complex HTML.
  • Keep transactional and promotional streams separate by subdomain and message design.
  • Stop sending to hard bounces and complaints.

Avoid putting a user’s raw email address, reset token, or sensitive account data into analytics tags. Tags are useful for grouping messages by type or internal entity, but they may be visible in provider logs and operational tools. Use an order ID or an internal opaque identifier, not a secret.

Open and click tracking are disabled by default for Resend domains. That is often appropriate for account and security messages, where tracking adds little value and may create privacy or user-trust concerns. If you enable it for a particular use case, document why, ensure your privacy notices are accurate, and do not use open rate as the sole measure of delivery quality.

Troubleshooting common Resend transactional email failures

“You can only send testing emails to your own address”

This is typically a domain-verification issue. Resend allows limited testing before you verify a domain, but sending to arbitrary recipients requires a verified domain and a From address under that domain. Add the domain, publish the generated DNS records, wait for verification, and update the From address.

Domain verification is stuck in pending or failed

Compare every DNS record against the values shown in Resend. Check the hostname, record type, priority for MX records, and whether your DNS interface appended the domain name automatically. If records are correct but not visible publicly, allow for DNS propagation; Resend documents that this can occasionally take up to 72 hours before a recheck is warranted.

The API says sent, but the recipient has no message

First, inspect the Resend email record and webhook events. Determine whether the state is sent, delivered, delayed, bounced, failed, or suppressed. A delivered event means the recipient’s mail server accepted it; then ask the recipient to check spam, filtering rules, and alternate inbox tabs. A delayed event means wait for the temporary delivery issue to resolve rather than immediately generating a duplicate.

A customer received the same message twice

Investigate your retry logic and queue behavior. Add an idempotency key tied to the logical message event, use a database uniqueness constraint, and record the returned Resend email ID. Do not add a new random idempotency key on retries.

The webhook endpoint receives events but your code rejects them

Confirm that the endpoint reads the exact raw body used for signature verification. Middleware that parses and reserializes JSON before verification is a common source of invalid-signature failures. Also verify the webhook signing secret belongs to the specific configured webhook, not an API key or another environment.

An address is unexpectedly suppressed

Look up the suppression origin: bounce, complaint, or manual. A hard bounce may indicate a typo, deleted mailbox, or permanent recipient-server block. Treat removal from suppression as a deliberate correction, not a routine retry button.

A deployment checklist for transactional email Resend

Before releasing a Resend integration, verify each item below:

  • A dedicated domain or subdomain is added and verified.
  • Resend-generated SPF and DKIM records are published exactly as shown.
  • A DMARC record is published and reports go to a monitored mailbox or reporting service.
  • Production uses a separate, server-side API key with sending-only and domain-scoped access where appropriate.
  • The API key is stored in a secret manager or server environment variable.
  • Every critical email type has a stable idempotency key.
  • Your database records the business event and Resend email ID.
  • A signed webhook endpoint records delivery, bounce, complaint, and suppression events.
  • Webhook deliveries are deduplicated with a unique provider event ID.
  • You tested delivered, bounced, complained, and suppressed cases with Resend’s test recipients.
  • Hard bounces and complaints stop future nonessential mail.
  • Email content has both HTML and plain-text versions.
  • Logs and alerts exist for spikes in failures, bounces, or webhook verification errors.

Conclusion

The right way to implement transactional email with Resend is to build a small delivery system around the send API. Verify a domain you own, use a purpose-specific subdomain, protect a scoped server-side API key, send only after the relevant business event is safe, and attach a stable idempotency key.

Then close the loop with verified webhooks. Record what Resend accepted, what recipient servers delivered or rejected, and which addresses were suppressed. When you test both the happy path and the failure paths, your transactional email becomes measurable, recoverable, and much less likely to create duplicate-message or missing-message support problems.

FAQ

Can I use Resend for password resets and magic links?

Yes. Those are standard transactional email use cases. Generate the reset or login token in your own backend, make it short-lived according to your security requirements, send it from a verified domain, and use an idempotency key tied to the reset or login event.

Does a successful Resend API response mean the recipient got the email?

No. It means Resend accepted the send request and returned an email ID. Use webhooks and the email log to distinguish sent, delivered, delayed, bounced, failed, and suppressed outcomes. Even delivered means the recipient’s mail server accepted it, not necessarily that the recipient read it.

Do I need DMARC for Resend transactional email?

You should publish DMARC as part of a production authentication posture. Google requires SPF or DKIM for all senders to personal Gmail accounts and requires SPF, DKIM, and DMARC for senders above its 5,000-messages-per-day threshold to personal Gmail accounts. Begin with an observed policy if needed, then strengthen it after confirming all legitimate mail sources authenticate correctly.

How do I stop Resend from sending duplicate transactional emails?

Use Resend idempotency keys on send requests and use a database record with a unique constraint for each logical message event. Resend retains idempotency keys for 24 hours, so your own durable message record should remain the longer-term source of truth.

Should I use Resend’s API or SMTP?

Use the API for new application development when you want structured responses, SDK support, and direct access to provider capabilities. Use SMTP when an existing platform or application already supports SMTP and replacing its transport is the lowest-risk integration path. Both approaches still need a verified domain, secure credential handling, retries that avoid duplicates, and event monitoring.