# Migrate from Resend

The provider-specific half of the [migration guide](/docs/guides/email/migrate): how Resend's `POST /emails` payload, suppression handling, and Svix-signed webhooks map onto Bird. Do the steps in the main guide in order; this page is the lookup table for steps 1, 3, and 4. Of the providers covered, this is the shortest port: the payloads are nearly field-for-field identical.

## Map the send call

| What it does        | Resend                         | Bird                                                 |
| ------------------- | ------------------------------ | ---------------------------------------------------- |
| Sender              | `from`                         | `from`                                               |
| Recipients          | `to` / `cc` / `bcc` (max 50)   | `to` / `cc` / `bcc` (arrays)                         |
| Subject             | `subject`                      | `subject`                                            |
| Body                | `html` / `text`                | `html` / `text` (at least one)                       |
| Reply-to            | `reply_to`                     | `reply_to` (array)                                   |
| Custom headers      | `headers`                      | `headers` (string → string object)                   |
| Filterable labels   | `tags`: `{name, value}` pairs  | `tags`: `{name, value}` pairs                        |
| Round-trip context  | (none; tags double as context) | `metadata`: arbitrary JSON                           |
| Scheduling          | `scheduled_at`                 | `scheduled_at`                                       |
| Open/click tracking | per-domain dashboard setting   | `track_opens` / `track_clicks` (default `true`)      |
| Category            | (none)                         | `category`: `marketing` (default) or `transactional` |

Bird-side field caps and defaults (recipient counts, tag and metadata limits) live in [Sending email](/docs/guides/email/sending-email).

Porting notes:

- **Tags keep their shape, and `metadata` is an upgrade.** Resend tags are the same `{name, value}` pairs Bird uses, but their value constraints pushed correlation data into tag values. On Bird, move correlation context into `metadata` (arbitrary JSON, echoed back on every webhook event and returned on API reads) and keep tags for filtering. See [tags vs metadata](/docs/guides/email/sending-email#tags-vs-metadata).
- **Tracking moves into the payload.** Resend toggles open/click tracking per domain in the dashboard; Bird sets `track_opens`/`track_clicks` per message (both default `true`).
- **`scheduled_at` maps directly**, name and all; see [scheduled sending](/docs/guides/email/scheduled-sending). For `react`, render your React Email templates to HTML in your application (the `render` function from `@react-email/render` works unchanged) and send the result as `html`.
- **Attachments port directly.** Resend's `attachments` (base64 `content`) map to Bird's [attachments](/docs/guides/email/attachments) array; set `content_id` for inline images.
- **Batch sending ports directly.** Resend's `POST /emails/batch` becomes Bird's [batch endpoint](/docs/guides/email/sending-bulk), with per-entry results in both cases.

## Export suppressions

Resend doesn't expose a dedicated suppression-list export. Pull the addresses whose last event is `bounced` or `complained`, from the Emails view in the dashboard or by walking your stored webhook events if you've been recording them, and run the list through the [import loop](/docs/guides/email/migrate#3-import-suppressions). If you use Audiences for marketing mail, also carry over contacts marked unsubscribed.

## Translate webhook events

| Outcome            | Resend                   | Bird                                         |
| ------------------ | ------------------------ | -------------------------------------------- |
| Accepted/processed | `email.sent`             | `email.accepted` → `email.processed`         |
| Delivered          | `email.delivered`        | `email.delivered`                            |
| Temporary failure  | `email.delivery_delayed` | `email.deferred`                             |
| Permanent bounce   | `email.bounced`          | `email.bounced` / `email.out_of_band_bounce` |
| Spam complaint     | `email.complained`       | `email.complained`                           |
| Blocked/suppressed | `email.failed`           | `email.rejected`                             |
| Open               | `email.opened`           | `email.opened`                               |
| Click              | `email.clicked`          | `email.clicked`                              |
| Unsubscribe        | (none)                   | `email.list_unsubscribed`                    |

The signing scheme barely changes: Resend delivers through Svix (`svix-id`, `svix-timestamp`, `svix-signature` headers), and Bird signs per the [Standard Webhooks](https://www.standardwebhooks.com) specification, which uses the same HMAC construction with `webhook-*` header names. If you verify Resend deliveries today, the same code verifies Bird's after renaming the three headers; or use the recipe in [Webhooks & events](/docs/guides/webhooks).

One behavioral difference: Resend's events are message-scoped; Bird's delivery events are **recipient**-scoped (`recipient_id` alongside `email_id`), so a three-recipient send produces three delivery outcomes, not one.

## Cut over

Work through [domains & DNS](/docs/guides/email/migrate#2-re-point-domains-and-dns) and the [sandbox smoke test](/docs/guides/email/migrate#5-verify-in-the-sandbox-before-cutover) in the main guide; both are provider-independent.

## Next steps

- [Sending domains](/docs/guides/email/sending-domains): registration, verification lifecycle, and the DNS records you're re-pointing
- [Webhooks & events](/docs/guides/webhooks): endpoint setup and Standard Webhooks verification
- [Testing sandbox](/docs/guides/email/testing-sandbox): smoke-test the new integration before cutover
- [Suppressions](/docs/guides/email/suppressions): confirm your imported list and how Bird maintains it from here