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.
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.
| Resend | Sendara | Notes |
|---|---|---|
| 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) | Domain | POST /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. |
| from | from | The 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.send | sendara.emails.send | The { from, to, subject, html } shape is the same. |
| Webhook events (email.*) | Webhook events | Subscribe at POST /v1/webhooks. Sendara signs each delivery with HMAC-SHA256. |
| Suppression list | Suppression list | Import hard bounces and complaints with POST /v1/suppressions/import (state suppressed). Import marketing opt-outs with state unsubscribed so transactional email remains allowed. |
| Audiences / Broadcasts | Contact lists / Broadcasts | Call 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| Resend | Sendara |
|---|---|
| RESEND_API_KEY | SENDARA_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.
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>",
});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" }'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" }
]
}'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 event | Sendara event |
|---|---|
| email.delivered | delivered |
| email.bounced | bounced |
| email.complained | complained |
| email.opened | opened |
| email.clicked | clicked |
| email.delivery_delayed | delivery_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.
- Verify the domain in Sendara and publish the DKIM, SPF, DMARC, and MAIL FROM records, leaving Resend verified.
- Import your suppression list via
POST /v1/suppressions/importso no known-bad address is ever mailed. - Wire up the SDK and webhooks against a test key, remapping the events from the table above.
- 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. - 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.
- Cut over the remainder, keep Resend verified for a week as a fallback, then decommission it.
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.