Skip to content
YourMail

Migration guide

Migrate from Resend

The send call maps almost field for field: the same from, to, subject and html, the same tag shape, the same Idempotency-Key header, and the snake_case names Resend uses are accepted as aliases. Most of a migration is the part around the code — the domain, the suppression list and the timing. If you are still deciding, read YourMail vs Resend first; this page assumes you have decided.

What to move first

In this order, and all of it before the first live send:

  1. Verify the domain. Add it and publish the three DKIM CNAME records described in Domains. The old provider's records can stay where they are until you are finished with it.
  2. Import the suppression list. Every address Resend has stopped sending to is one you should not mail from a fresh account on day one. See below.
  3. Make a test send. The quickstart covers the key and a first send from the shared test sender.
  4. Change the call sites, one touchpoint at a time if you like. Both providers can run at once.

Mind the timing. A new account's daily cap ramps up over its first seven days of live sending, counted in recipients — the stages are on Limits. Real transactional traffic rarely meets it, but a launch-day spike can, so cut over ahead of a launch, not on the morning of one.

The send call

The SDK is npm i yourmail. Two differences worth knowing in the code itself: options are camelCase (the snake_case forms are still accepted), and the SDK throws a typed error instead of returning { data, error }, so an if (error) branch becomes a catch.

import { Resend } from "resend";

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

const { data, error } = await resend.emails.send({
  from: "Acme <hello@acme.com>",
  to: ["customer@example.com"],
  subject: "Your receipt",
  html: "<p>Thanks for your order.</p>",
  reply_to: "support@acme.com",
  tags: [{ name: "category", value: "receipt" }],
});

Resend's POST https://api.resend.com/emails becomes POST https://api.yourmail.dev/v1/emails, authenticated with Authorization: Bearer and your YourMail key. The response is { "id": "…" }. The full reference is Send email.

Field by field

Resend's field names below are as of October 2026, taken from their send-email reference (opens in a new tab); check it for anything that has changed since.

from, to, subjectsame
Same names and shapes. Display names ("Acme <hello@acme.com>") work in every address field. At most 50 recipients across to, cc and bcc combined.
html, textsame
Same. One or both; 1 MB combined.
reactsame (SDK)
Supported in the SDK in place of html, via the optional @react-email/render peer dependency. Your react-email templates move unchanged.
cc, bccsame
A string or an array.
reply_to→ replyTo
Accepted under either name. One address — Resend also takes an array here; YourMail takes a single string.
headerssame
An object of string values. Up to 10; headers the pipeline owns (From, To, Subject, Content-Type and the like) are refused with a 400.
tagssame
An array of { name, value }, up to 10. An object is a 400.
attachmentspartly
filename and content (base64) carry over; content_type is accepted as an alias of contentType. Resend's path (a remote URL) and content_id have no equivalent: send the bytes. Up to 20 files, 10 MB each, 25 MB total.
scheduled_at→ scheduledAt
Accepted under either name. ISO 8601 or Unix milliseconds, up to 30 days ahead; Unix seconds are a 400. Cancel with yourmail.cancel(id) until it dispatches.
Idempotency-Keyheader
Same header, same job. A body idempotencyKey (or idempotency_key) also works and wins over the header. The SDK sets one for you so its own retries cannot send twice.
templatenone
No stored templates. Render the HTML in your own code — react-email, or whatever you use today — and send the result.

Batch sends

Resend's POST /emails/batch takes a bare array of up to 100 messages (as of October 2026, per their batch reference (opens in a new tab)). POST /v1/emails/batch takes the same bare array, or { emails: [...] }, also up to 100. An Idempotency-Key header derives a key per item, so a replayed batch returns the same ids in the same order.

// A bare array, as Resend's batch endpoint takes, is accepted here too —
// so is { emails: [...] }. Up to 100 per request.
const { data, summary } = await yourmail.batch(
  orders.map((order) => ({
    from: "Acme <hello@acme.com>",
    to: order.email,
    subject: "Your receipt",
    html: renderReceipt(order),
  })),
);
// Always a 200: one bad item reports its own error. Check summary.failed.
if (summary.failed > 0) console.error(data.filter((r) => "error" in r));

Items are validated and queued independently, and the response is a 200 whichever way they went — read summary.failed, not the status code. Details on Batch send.

Bring the suppression list

Resend keeps a suppression list of addresses that hard-bounced or complained, and (as of October 2026) lets you download it as a CSV from its Suppressions page or list it through its API — see their suppressions documentation (opens in a new tab). Import those addresses with POST /v1/suppressions before you send anything live:

import { YourMail, MAX_SUPPRESSION_IMPORT } from "yourmail";

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY); // full-scope key

// `exported`: the addresses from Resend's suppression list (CSV or API).
for (let i = 0; i < exported.length; i += MAX_SUPPRESSION_IMPORT) {
  await yourmail.createSuppressions(exported.slice(i, i + MAX_SUPPRESSION_IMPORT));
}

An import is strict — one invalid address fails that request with a 400 naming it — and is recorded with the reason manual, which blocks every send. Why this step matters more than it looks is in Bounces and suppression lists.

Webhooks

Re-register your endpoint with YourMail and change the verification. Resend signs deliveries with svix-id, svix-timestamp and svix-signature headers (as of October 2026, per their verification guide (opens in a new tab)). YourMail sends a single yourmail-signature header, and the SDK's constructWebhookEvent verifies it against the raw body. Event names are email.sent, email.delivered, email.bounced, email.complained and the rest listed in Webhooks; map each handler by name rather than assuming the payloads match.

What has no equivalent here

Broadcasts, audiences and contacts. YourMail is transactional sending only and has no marketing product. If you use those features, keep them where they are — or keep Resend.

Stored templates. Nothing is rendered server-side; you send finished HTML, or a React element through the SDK. If your templates are already react-email components, they move without change.

Attachments by URL. Send the file's bytes instead of a path.

For non-transactional mail you do still send through the API, set bulk: true: it adds one-click List-Unsubscribe headers and honours unsubscribes, which then block bulk mail only.

Common questions

Do I need to re-verify my sending domain?

Yes. Verification proves to the sending provider that you control the domain, so it does not carry over between providers. YourMail asks for three DKIM CNAME records, shown when you add the domain in the dashboard or with POST /v1/domains. Nothing in that setup asks you to remove Resend's records, so both can be verified at the same time while you cut over.

Can I keep my Resend suppression list?

Yes, and you should import it before the first live send. Export the list from Resend, then POST the addresses to /v1/suppressions — up to 100 per request, or createSuppressions() in the SDK. Imported addresses are recorded with the reason manual, which blocks every send to them.

Can I run Resend and YourMail side by side during the cutover?

Yes. Nothing stops the same domain being verified with both, so you can move one touchpoint at a time — password resets first, say — and keep the rest on Resend until each has been seen working. Plan the timing around the new-account ramp: a YourMail account's daily limit rises over its first week of live sending, so cut over ahead of a launch rather than on the morning of it.

What does not move from Resend?

Broadcasts, audiences and contact management, because YourMail has no marketing product. Stored templates, because YourMail renders nothing server-side — you send finished HTML or a React element. And attachments by remote path, because YourMail takes the file's bytes.

Switching from another provider?

Last updated