Migrate from Resend

Migrating from Resend to Sendpository

Everything that changes when you move your email from Resend, in the order you'll do it. Usually under an hour for the code, plus DNS propagation.

In short

Moving from Resend to Sendpository is mostly a key and base-URL change: both take a JSON body with from, to, subject and html over HTTPS with a Bearer key, both honour an Idempotency-Key header, and event names are almost the same. The three real changes are tags (an array of name/value pairs becomes a plain object), webhook verification (Svix headers become one Sendpository-Signature header) and re-verifying your domain.

Or let your coding agent do the whole migration

Paste it in. It finds every Resend call, rewrites it, updates your webhook handler and switches over behind a flag you can flip back.

Show the prompt

Switch this project's email from Resend to Sendpository. First run `npx sendpository@latest init` - it opens my browser for me to approve, saves the API key and installs the Sendpository skill - then follow the skill's migration guide (references/migrate.md). If you can't run commands, read https://www.sendpository.com/agents/sendpository/references/migrate.md instead.

1. API differences

ResendSendpository
EndpointPOST https://api.resend.com/emailsPOST https://api.sendpository.com/v1/emails
BodyJSON: from, to, subject, html, text, cc, bcc, reply_to, headers, attachments, scheduled_atThe same field names. to, cc, bcc and reply_to take arrays; a single string is accepted for to.
TagsArray of { name, value }Plain object: { "type": "receipt" }
Templatesreact (Node SDK) or a stored template id with variablesSend rendered HTML. React Email works: render the component to HTML and pass it as html.
IdempotencyIdempotency-Key headerIdempotency-Key header - a replay returns the original id
BatchPOST /emails/batchPOST https://api.sendpository.com/v1/emails/batch, up to 100 messages, each accepted or rejected on its own
Response{ "id": "..." }{ "id": "..." }

2. Authentication differences

ResendSendpository
API keyAuthorization: Bearer re_...Authorization: Bearer sp_... - create a key with the sending permission for the app, full_access only where you manage domains or webhooks by API.

Create the key under API keys in the dashboard. It is shown once, so put it straight into SENDPOSITORY_API_KEY in your secrets manager. Keep the Resend key until the switch is finished, then revoke it.

3. Domain setup

Add your sending domain in Sendpository and publish the records it shows: a 2048-bit DKIM key signed as your own domain, plus SPF and a bounce MX on a sp. subdomain so your existing records are never edited. DMARC is recommended; if you already publish one, leave it as it is.

  • Add the same domain in Sendpository. Its SPF and bounce records go on a sp. subdomain, so they don't collide with anything Resend asked you to publish.
  • The DKIM record has a different selector, so it sits alongside Resend's. Both can exist while you switch over.

Not sure what’s published already? Run the domain through the email DNS checker.

4. Webhooks

ResendSendpository
VerificationSvix: svix-id, svix-timestamp and svix-signature headersOne Sendpository-Signature header: t=<unix>,v1=<HMAC-SHA256 of "t.rawBody">. The SDK's verifyWebhook checks it and rejects anything older than five minutes.

Event names

Resend eventSendpository event
email.sentemail.sent
email.deliveredemail.delivered
email.delivery_delayedemail.delayed
email.bouncedemail.bounced
email.complainedemail.complained
email.failedemail.failed
email.openedemail.opened
email.clickedemail.clicked
email.scheduledemail.scheduled
email.receivedemail.received

The payload is { type, created_at, data } with data.email_id identifying the message.

Full payloads and verification code are in the webhooks guide.

5. Error handling

ResendSendpository
Error shapeAn error object with a name and a message.{ "error": { "type": "domain_not_verified", "message": "..." } }

Map your checks on Resend's error name to our error.type. validation_error, rate_limit_exceeded and not_found keep their meaning. Only rate_limit_exceeded (wait for Retry-After) and internal_error are worth retrying automatically; the errors reference lists every type.

6. Code migration

Before - Resend (Node.js)
import { Resend } from "resend";

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

const { data, error } = await resend.emails.send({
  from: "Acme <billing@yourdomain.com>",
  to: ["customer@example.com"],
  subject: "Your receipt",
  html: "<p>Thanks for your order.</p>",
  tags: [{ name: "type", value: "receipt" }],
});
After - Sendpository (Node.js)
import { Sendpository, SendpositoryError } from "sendpository";

const sendpository = new Sendpository(process.env.SENDPOSITORY_API_KEY);

try {
  const { id } = await sendpository.emails.send(
    {
      from: "Acme <billing@mail.yourdomain.com>",
      to: ["customer@example.com"],
      subject: "Your receipt",
      html: "<p>Thanks for your order.</p>",
      text: "Thanks for your order.",
      tags: { type: "receipt" },
    },
    // A retried job returns the first message's id instead of sending twice.
    { idempotencyKey: "receipt-10428" },
  );
  console.log(id);
} catch (err) {
  if (err instanceof SendpositoryError) console.error(err.type, err.message);
  throw err;
}

Watch out for

7. Pricing

ResendSendpository
Published pricingFree: 3,000 emails a month, capped at 100 a day, one domain. Paid plans start at $20 a month for 50,000 emails.6,000 emails a month (up to 200 a day), free forever. Current plans

Resend pricing from their own pages, checked 2026-09-23. Sendpository's free plan is larger: 6,000 emails a month (up to 200 a day), against Resend's 3,000 a month.

8. Things to test before switching

Specific to leaving Resend:

True of any migration:

Questions

Can I keep React Email after moving from Resend?

Yes. Render the component with React Email's render function and pass the resulting HTML as the html field. Sendpository sends whatever HTML you give it.

Do I need to change my webhook handler?

Yes, the signature check. Resend signs with Svix headers; Sendpository sends one Sendpository-Signature header, verified with verifyWebhook from the sendpository package. Event names are the same except email.delivery_delayed, which is email.delayed.

Can I run Resend and Sendpository side by side?

Yes. The DKIM records use different selectors and Sendpository's SPF and bounce records live on a subdomain, so both can be verified at once while you move traffic over.

Resend API details are from their documentation, checked 2026-09-25: resend.com, resend.com, resend.com.

Keep reading

Send your first email in minutes

6,000 emails a month (up to 200 a day), free forever. No card needed.