Migrate

Migrate from Resend

Everything that maps from Resend to Sendara: the concept map, a before/after send, your suppression list, webhook events, and a parallel-run cutover you can do without a deliverability scare.

Sendara mirrors the surface you already use in Resend: an API key, a verified sending domain, a send call shaped like { from, to, subject, html }, an event timeline, and signed webhooks. The migration is mostly mechanical. This page maps each piece. For the why and the overall strategy, start with Switch to Sendara.

Keep your Resend account live and verified throughout. You can verify the same sending domain in both at once and run them in parallel, so nothing here is a one-way door until you decide to decommission.

Why switch

You send from your own domain with your own DKIM, SPF, and DMARC, so your reputation moves with you. Switching providers neither resets nor repairs it. What changes is the developer experience around the send: idempotent requests, a full per-message event timeline, an open suppression model you import and control, and pricing that does not penalize growth. It is a clean API and an honest billing model. It is not a deliverability cure.

Concept map

Here is how the Resendprimitives you rely on line up with Sendara's.

ResendSendaraNotes
API key (re_…)API key (sk_live_… / sk_test_…)Create a key in the dashboard. A test key simulates the delivery, and Sendara charges nothing for it.
Domain (verify in dashboard)DomainPOST /v1/domains returns the 6 records to publish: 3 DKIM CNAMEs, an MX record and an SPF record on the MAIL FROM subdomain, and a DMARC record.
fromfromThe shape is the same. Give a bare address, or a display-name address such as 'Acme <[email protected]>'. The SDK sends it as metadata.from_email.
resend.emails.sendsendara.emails.sendThe { from, to, subject, html } shape is the same.
Webhook events (email.*)Webhook eventsSubscribe at POST /v1/webhooks. Sendara signs each delivery with HMAC-SHA256.
Suppression listSuppression listImport hard bounces and complaints with POST /v1/suppressions/import (state suppressed). Import marketing opt-outs with state unsubscribed so transactional email remains allowed.
Audiences / BroadcastsContact lists / BroadcastsCall sendara.contacts.lists.* and sendara.broadcasts.*.

Swap the env vars & SDK

Install the Sendara SDK and point your code at a Sendara API key. Create one in the dashboard. Test keys (sk_test_…) simulate delivery and never bill, so you can wire everything up before sending a single real message.

npm install sendara
ResendSendara
RESEND_API_KEYSENDARA_API_KEY

Before & after: sending

The send call keeps the same shape. Set the sender to an address on your verified domain with the SDK from parameter. On the wire, that value travels as metadata.from_email. Each SDK call generates an idempotency key. Automatic in-call retries reuse it; a retry started by your application must pass and reuse the same explicit key.

before.ts
import { Resend } from "resend";

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

await resend.emails.send({
  from: "Acme <[email protected]>",
  to: "[email protected]",
  subject: "Welcome to Acme",
  html: "<strong>It works!</strong>",
});
To show a friendly name, pass a display-name address: from: "Acme <[email protected]>" (name first, address in angle brackets). See sender name.

DNS & domain verification

Verify your domain in Sendara with POST /v1/domains. Publish the 6 records that Sendara returns: 3 DKIM CNAMEs, an MX record and an SPF record on the MAIL FROM subdomain, and a DMARC record. Keep your Resend domain verified, so that you can run both providers together. Move your traffic to Sendara when you are ready.

curl https://api.sendara.dev/v1/domains \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "mail.acme.com" }'
Verify in Sendara without removing anything from Resend. DKIM supports multiple selectors and SPF can include multiple senders, so both providers can be authorized for the same domain at the same time. See Domains & deliverability.

Import your suppression list

Export your Resend suppression list. Import hard bounces and complaints with the default state suppressed; mark marketing opt-outs as unsubscribed. Sendara then suppresses every new hard bounce and complaint automatically.

Import in bulk with POST /v1/suppressions/import. It is an admin-scoped, idempotent endpoint that upserts each entry and returns { imported, skipped }, so re-running the import is safe. Use state: "suppressed" (the default) for hard bounces, complaints, and manual hard blocks; use state: "unsubscribed" for marketing opt-outs so transactional mail remains allowed. The import does not rewrite a matching contact record. After cutover, hard bounces and spam complaints are added to the list automatically. You can also manage individual entries with POST/DELETE /v1/suppressions.

curl https://api.sendara.dev/v1/suppressions/import \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      { "email": "[email protected]", "reason": "hard_bounce", "state": "suppressed" },
      { "email": "[email protected]", "reason": "provider_unsubscribe", "state": "unsubscribed" }
    ]
  }'
Import suppressions before your first production send on Sendara. A send to a suppressed address is recorded as suppressed and never enqueued or billed. That is the safety net that stops a fresh provider from re-mailing the addresses your old one already learned to avoid.

Map your webhook events

Resend signs with the Svix headers. Sendara signs an HMAC-SHA256 of `${timestamp}.${rawBody}` in hex. Check the signature with verifyWebhook() from the SDK. Map each email.* type to the matching Sendara event type.

Resend eventSendara event
email.delivereddelivered
email.bouncedbounced
email.complainedcomplained
email.openedopened
email.clickedclicked
email.delivery_delayeddelivery_delayed

Subscribe with POST /v1/webhooks, then verify each delivery with verifyWebhook() from the SDK. See Webhooks and Events & the message timeline for the full payload schemas and delivery guarantees.

Cutover checklist

Run this in order. You can stop at any step and stay there as long as you like. Nothing is irreversible until you decommission Resend.

  1. Verify the domain in Sendara and publish the DKIM, SPF, DMARC, and MAIL FROM records, leaving Resend verified.
  2. Import your suppression list via POST /v1/suppressions/import so no known-bad address is ever mailed.
  3. Wire up the SDK and webhooks against a test key, remapping the events from the table above.
  4. Mirror a low-stakes stream through Sendara and watch it in the message log. Confirm each send with GET /v1/messages?idempotency_key=… and compare delivered, bounced, and complained rates against Resend.
  5. Ramp traffic up as the numbers hold. There are no IPs to warm when you stay on the same domain, so move as fast as your confidence allows.
  6. Cut over the remainder, keep Resend verified for a week as a fallback, then decommission it.
The idempotency_key you already pass on each send is also your status handle. Behind a feature flag, you can replay a request against Sendara without double-sending, then look it up with GET /v1/messages?idempotency_key=… to confirm it delivered. See Message lifecycle.