# Migrate from SendGrid

The provider-specific half of the [migration guide](/docs/guides/email/migrate): how SendGrid's v3 Mail Send payload, suppression lists, and Event Webhook map onto Bird. Do the steps in the main guide in order; this page is the lookup table for steps 1, 3, and 4.

## Map the send call

SendGrid's `POST /v3/mail/send` wraps recipients in a `personalizations` array; Bird's [`POST /v1/email/messages`](/docs/api/reference/create-email-message) is a flat payload, so each personalization becomes its own send (or one [batch entry](/docs/guides/email/sending-bulk)).

| What it does        | SendGrid                                | Bird                                                 |
| ------------------- | --------------------------------------- | ---------------------------------------------------- |
| Sender              | `from.email`                            | `from`                                               |
| Recipients          | `personalizations[].to` / `cc` / `bcc`  | `to` / `cc` / `bcc` (arrays)                         |
| Subject             | `subject`                               | `subject`                                            |
| Body                | `content[]` (type + value)              | `html` / `text` (at least one)                       |
| Reply-to            | `reply_to` / `reply_to_list`            | `reply_to` (array)                                   |
| Custom headers      | `headers`                               | `headers` (string → string object)                   |
| Filterable labels   | `categories`                            | `tags`: `{name, value}` pairs                        |
| Round-trip context  | `custom_args`                           | `metadata`: arbitrary JSON                           |
| Stored template     | `template_id` + `dynamic_template_data` | `template` + `template.parameters`                   |
| Scheduling          | `send_at`                               | `scheduled_at`                                       |
| Open/click tracking | `tracking_settings`                     | `track_opens` / `track_clicks` (default `true`)      |
| IP pool             | `ip_pool_name`                          | `ip_pool_id` (`ipp_...` or `ipp_shared`)             |
| 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:

- **`categories` are bare strings; Bird tags are pairs.** A category like `"welcome"` becomes `{"name": "category", "value": "welcome"}`. Pick a stable `name` so your dashboards filter the way your SendGrid stats did.
- **`custom_args` were echoed in every event; Bird `metadata` works the same way.** Bird echoes your `metadata` (and `tags`) on every webhook event alongside `email_id`/`recipient_id`, so your handlers get your context back without an extra lookup.
- **Dynamic templates port to stored templates.** `template_id` plus `dynamic_template_data` become `template` (referenced by ID or name) plus `template.parameters` on the same send call; see [sending with a template](/docs/guides/email/sending-email#sending-with-a-template). Template authoring on Bird is in preview and not yet open to every workspace, so until yours has it, render content in your application and send `html`/`text`. `send_at` maps to [`scheduled_at`](/docs/guides/email/scheduled-sending) directly.
- **Attachments port directly.** SendGrid's `attachments` (base64 `content`, `type`, `filename`, `content_id` for inline) map to Bird's [attachments](/docs/guides/email/attachments) array field-for-field.
- **Unsubscribe groups (`asm`)** don't port as a concept: Bird handles list-unsubscribe at the [category](/docs/guides/email/categories) level, so `marketing` mail gets suppression-aware unsubscribe handling automatically.

## Export suppressions

SendGrid splits suppressions across endpoints; export each and run them through the [import loop](/docs/guides/email/migrate#3-import-suppressions):

- `GET /v3/suppression/bounces`
- `GET /v3/suppression/spam_reports`
- `GET /v3/suppression/unsubscribes` (global unsubscribes)
- `GET /v3/asm/groups/{group_id}/suppressions` for each unsubscribe group you want to carry over

## Translate webhook events

| Outcome            | SendGrid Event Webhook              | Bird                                             |
| ------------------ | ----------------------------------- | ------------------------------------------------ |
| Accepted/processed | `processed`                         | `email.accepted` → `email.processed`             |
| Delivered          | `delivered`                         | `email.delivered`                                |
| Temporary failure  | `deferred`                          | `email.deferred`                                 |
| Permanent bounce   | `bounce`                            | `email.bounced` / `email.out_of_band_bounce`     |
| Spam complaint     | `spamreport`                        | `email.complained`                               |
| Blocked/suppressed | `dropped`                           | `email.rejected`                                 |
| Open               | `open`                              | `email.opened`                                   |
| Click              | `click`                             | `email.clicked`                                  |
| Unsubscribe        | `unsubscribe` / `group_unsubscribe` | `email.unsubscribed` / `email.list_unsubscribed` |

The `dropped` ↔ `email.rejected` equivalence is the one to test: like SendGrid, Bird reports suppressed recipients visibly (status `rejected`, `rejection_reason: recipient_suppressed`) rather than silently dropping them, so your audit logic ports cleanly.

Verification changes more than the event names: SendGrid's Event Webhook signs with an ECDSA public key, while Bird signs per the [Standard Webhooks](https://www.standardwebhooks.com) HMAC scheme. Swap your verification code for the recipe in [Webhooks & events](/docs/guides/webhooks). SendGrid also batches events into JSON arrays; Bird delivers one event per request.

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