# Migrate from Mandrill

The provider-specific half of the [migration guide](/docs/guides/email/migrate): how Mandrill's (Mailchimp Transactional) `messages/send` payload, rejection blacklist, and 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.

Two shape changes dominate the port. Mandrill nests everything under a `message` object and authenticates with a `key` in the request body; Bird takes a flat top-level payload and a standard `Authorization: Bearer` header. And Mandrill's recipient `type` (`to`/`cc`/`bcc` as a field on each address) becomes Bird's separate `to`/`cc`/`bcc` arrays.

## Map the send call

| What it does        | Mandrill (`messages/send`)                        | Bird                                                  |
| ------------------- | ------------------------------------------------- | ----------------------------------------------------- |
| Auth                | `key` in request body                             | `Authorization: Bearer bk_...` header                 |
| Sender              | `message.from_email` / `from_name`                | `from` — string or `{ "email", "name" }`              |
| Recipients          | `message.to` — `[{ email, name, type }]`          | `to` / `cc` / `bcc` — split by the `type` field       |
| Subject             | `message.subject`                                 | `subject`                                             |
| Body                | `message.html` / `message.text`                   | `html` / `text` (at least one)                        |
| Reply-to            | `message.headers["Reply-To"]`                     | `reply_to` — array, 1–25                              |
| Custom headers      | `message.headers`                                 | `headers` — string → string object                    |
| Filterable labels   | `message.tags` — bare strings                     | `tags` — `{ name, value }` pairs, max 20              |
| Round-trip context  | `message.metadata`                                | `metadata` — arbitrary JSON, max 2 KB                 |
| Open/click tracking | `message.track_opens` / `track_clicks`            | `track_opens` / `track_clicks` (default `true`)       |
| Attachments         | `message.attachments` — `{ type, name, content }` | `attachments` — `{ content_type, filename, content }` |
| Inline images       | `message.images` — `{ type, name, content }`      | `attachments` with `content_id`                       |
| Category            | subaccount / tags convention                      | `category`: `marketing` (default) or `transactional`  |

Porting notes:

- **Flatten and re-auth.** Drop the `message` wrapper — its fields move to the top level — and move the API key out of the body into the `Authorization` header. The `key` field has no Bird equivalent.
- **Split recipients by `type`.** Mandrill marks each recipient `to`, `cc`, or `bcc` on the address object; Bird uses three separate arrays. Bucket the `to` list by its `type` field as you port.
- **Tags become name/value pairs.** Mandrill tags are bare strings (`"welcome"`); Bird tags are `{ name, value }` pairs. Pick a stable `name` — `{ "name": "category", "value": "welcome" }` — so your filters and analytics group the way your Mandrill stats did. `metadata` ports across directly as JSON.
- **`merge_vars` / templates render in your app.** Mandrill's handlebars-style merge tags and stored templates (`messages/send-template`) have no Bird equivalent yet — `template_id` is [reserved](/docs/guides/email/sending-email#reserved-fields). Render the final HTML in your application and send it as `html`.
- **`send_at` maps to `scheduled_at`.** Mandrill's scheduled-send `send_at` maps to Bird's [`scheduled_at`](/docs/guides/email/scheduled-sending).
- **Attachments port directly** — Mandrill's base64 `content` is Bird's `content`, and inline `images` (referenced as `cid:` in the HTML) become `attachments` entries with `content_id`. See [attachments](/docs/guides/email/attachments).

## Export suppressions

Mandrill keeps unwanted addresses on its **rejection blacklist** — pull it with the `rejects/list` API (or export from the Rejection Blacklist view). Each entry carries a reason (`hard-bounce`, `soft-bounce`, `spam`, `unsub`, `custom`); skip the `soft-bounce` rows (transient, not a true suppression) and run the rest through the [import loop](/docs/guides/email/migrate#3-import-suppressions). Bird sorts them into its own [reasons](/docs/guides/email/suppressions) as they're imported.

## Translate webhook events

Mandrill posts batched event arrays; map the `event` value to Bird's [event vocabulary](/docs/guides/email/events):

| Outcome           | Mandrill      | Bird                                                   |
| ----------------- | ------------- | ------------------------------------------------------ |
| Sent / accepted   | `send`        | `email.accepted` → `email.processed`                   |
| Delivered         | —             | `email.delivered`                                      |
| Temporary failure | `deferral`    | `email.deferred`                                       |
| Permanent bounce  | `hard_bounce` | `email.bounced` / `email.out_of_band_bounce`           |
| Soft bounce       | `soft_bounce` | `email.deferred` (then `email.bounced` if it gives up) |
| Spam complaint    | `spam`        | `email.complained`                                     |
| Unsubscribe       | `unsub`       | `email.unsubscribed` / `email.list_unsubscribed`       |
| Rejected/blocked  | `reject`      | `email.rejected`                                       |
| Open              | `open`        | `email.opened`                                         |
| Click             | `click`       | `email.clicked`                                        |

Two differences to code for:

- **Bird reports delivery explicitly.** Mandrill's `send` event means the message was injected; Bird splits acceptance (`email.accepted`/`email.processed`) from the recipient mail server taking it (`email.delivered`).
- **Events are recipient-scoped and signed differently.** Bird's delivery events carry `recipient_id` alongside `email_id` (one stream per recipient), and Bird signs per the [Standard Webhooks](https://www.standardwebhooks.com) spec rather than Mandrill's `X-Mandrill-Signature` HMAC. See [Webhooks & events](/docs/guides/webhooks) for verification.

## 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