Skip to content
YourMail

Migration guide

Migrate from SendGrid

SendGrid's v3 Mail Send is built around personalizations: one request, many recipients, each with their own template data. YourMail has neither personalizations nor stored templates, so those two parts change shape — each personalization becomes one message, sent alone or in a batch, and each template becomes code you render yourself. The rest is a rename. If you are still deciding, read YourMail vs SendGrid first, including the part about what you lose.

Before you change any code

  1. Verify the domain with the three DKIM CNAME records in Domains. Leave SendGrid's records alone until you are done with it.
  2. Import the suppression list. See below — this is the step people skip.
  3. Make a test send following the quickstart.

Then plan the timing. A new account's daily cap ramps up over its first seven days of live sending, and it is counted in recipients, not messages — the stages are on Limits. A SendGrid integration that fans one request out to thousands of personalizations is exactly the shape that meets it, so cut over ahead of a launch, not on the morning of one.

The send call

await fetch("https://api.sendgrid.com/v3/mail/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    personalizations: [
      { to: [{ email: "ana@example.com" }], dynamic_template_data: { name: "Ana" } },
      { to: [{ email: "ben@example.com" }], dynamic_template_data: { name: "Ben" } },
    ],
    from: { email: "hello@acme.com", name: "Acme" },
    template_id: "d-xxxxxxxx",
    categories: ["welcome"],
    custom_args: { campaign: "onboarding" },
  }),
});

POST https://api.sendgrid.com/v3/mail/send answers 202 Accepted. POST https://api.yourmail.dev/v1/emails answers with the message's id, which you can look up later; a batch answers 200 with an id or an error per item, so read summary.failed. Both authenticate with a Bearer key. The SDK is npm i yourmail; references are Send email and Batch send.

Field by field

SendGrid's field names are as of October 2026, from their v3 Mail Send reference (opens in a new tab).

personalizations→ one send each
There is no personalizations array. Each personalization becomes its own message — a send, or an item in a batch of up to 100. One message carries at most 50 recipients across to, cc and bcc, and every one of them sees the same body.
personalizations[].to, cc, bcc→ to, cc, bcc
Arrays of { email, name } become strings: "Ana <ana@example.com>", or an array of them.
from→ from
{ email, name } becomes "Acme <hello@acme.com>". Must be on a domain you have verified with YourMail.
reply_to→ replyTo
One address, as a string. reply_to_list has no equivalent.
subject→ subject
Top level only. A per-personalization subject means a per-message send.
content→ html, text
The text/plain entry's value becomes text, the text/html one html. 1 MB combined.
template_id, dynamic_template_datanone
No stored templates. Render the body in your own code and send the result — see below.
categories→ tags
Each category becomes a { name, value } entry in tags. Up to 10 tags per message.
custom_args→ tags
Key/value pairs fold into the same tags array, sharing the 10-tag cap. Tags come back on the message and in webhook payloads.
headers→ headers
An object of string values, up to 10; headers the pipeline owns are refused with a 400.
attachments→ attachments
content (base64), filename and type become content, filename and contentType. disposition and content_id have no equivalent. Up to 20 files, 10 MB each, 25 MB total.
send_at→ scheduledAt
SendGrid takes a Unix timestamp in seconds. scheduledAt takes ISO 8601 or Unix milliseconds, up to 30 days ahead — Unix seconds are a 400, so multiply by 1000. batch_id has no equivalent; cancel one message with yourmail.cancel(id).
asm→ bulk
No unsubscribe groups. Non-transactional mail sets bulk: true, which adds one-click List-Unsubscribe headers and honours unsubscribes for bulk mail only.
tracking_settings, mail_settingsnone
No per-message settings. Open and click tracking is a per-domain setting, toggled in the dashboard.

Dynamic templates become your own rendering

A template_id with dynamic_template_data has no counterpart, because YourMail stores no templates. Move each template into your codebase and render it per recipient. If you write them as react-email components, the SDK renders them for you:

// Was: template_id "d-…" + dynamic_template_data { name }
// Now: a react-email component in your codebase.
import { Welcome } from "./emails/welcome";

await yourmail.send({
  from: "Acme <hello@acme.com>",
  to: user.email,
  subject: `Welcome, ${user.name}`,
  react: <Welcome name={user.name} />, // needs @react-email/render
});

Any other renderer works the same way — produce html (and ideally text) and send it. The React Email guide has the setup.

Bring the suppression list

SendGrid keeps separate suppression lists. As of October 2026 its API returns bounces from GET /v3/suppression/bounces, spam reports from GET /v3/suppression/spam_reports and global unsubscribes from GET /v3/suppression/unsubscribes, each entry carrying an email — see their bounces reference (opens in a new tab). Import the addresses with POST /v1/suppressions before your first live send:

import { YourMail, MAX_SUPPRESSION_IMPORT } from "yourmail";

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

// From SendGrid (paginate with limit/offset):
//   GET https://api.sendgrid.com/v3/suppression/bounces       → [{ email, … }]
//   GET https://api.sendgrid.com/v3/suppression/spam_reports  → [{ email, … }]
// collect the email fields into `exported`, then:
for (let i = 0; i < exported.length; i += MAX_SUPPRESSION_IMPORT) {
  await yourmail.createSuppressions(exported.slice(i, i + MAX_SUPPRESSION_IMPORT));
}

Bounces and spam reports are the ones that must come across. Global unsubscribes need a decision: every import is recorded as manual, and manual blocks all mail, so an imported unsubscribe would also stop that person's receipts and password resets. Why the list matters at all is in Bounces and suppression lists.

Webhooks

Point your event handling at a YourMail endpoint (dashboard, or POST /v1/webhooks). Each delivery is one event, signed in a yourmail-signature header that the SDK's constructWebhookEvent verifies against the raw body. Events are named email.delivered, email.bounced, email.complained, email.opened, email.clicked and so on, and carry the tags you set — so a handler keyed on a category reads it from the tags instead. See Webhooks.

What has no equivalent here

Marketing Campaigns, contacts and lists. YourMail is transactional sending only. bulk: true makes an individual non-transactional send compliant; it does not manage a list.

Stored templates, unsubscribe groups and per-message settings. Covered above: render in code, use bulk, and set tracking per domain.

Dedicated IPs and a deliverability team. Mail goes out through AWS SES, signed for your own domain. If those matter to you, the comparison page is candid about what you give up by leaving.

Common questions

Do I need to re-verify my sending domain?

Yes. Your domain authentication with SendGrid proves the domain to SendGrid only. 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 SendGrid's records, so both can be verified while you cut over.

Can I keep my SendGrid suppression list?

Yes, and it is the step that matters most. Export the bounces and spam reports through SendGrid's suppression API and import the addresses with POST /v1/suppressions — up to 100 per request, or createSuppressions() in the SDK — before the first live send. Imports are recorded with the reason manual, which blocks every send to that address.

How do I replace dynamic templates?

Render the body in your own code. YourMail stores no templates and has no template_id; you send finished html and text, or pass a React element through the SDK's react option, which renders a react-email component with the optional @react-email/render peer dependency.

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

Yes — move one touchpoint at a time and leave the rest on SendGrid until each is seen working. Plan the timing around the new-account ramp: a YourMail account's daily limit rises over its first week of live sending, counted in recipients, so cut over ahead of a launch rather than on the morning of it.

Switching from another provider?

Last updated