# Migreren vanuit MailerSend

Deze pagina koppelt de verzendpayload, suppressielijsten en webhooks van MailerSend aan Bird. Volg de [hoofdmigratiegids](/docs/guides/email/migrate) op volgorde en gebruik deze koppelingen voor stap 1, 3 en 4.

MailerSend heeft vijf suppressielijsten en één daarvan is bewust tijdelijk. Lees [Suppressies exporteren](#suppressies-exporteren) vóór stap 3: die ene importeren verandert een blokkade van 72 uur in een permanente blokkering.

## Geef dit aan je agent

Plak dit in Claude Code, Cursor of Codex. De agent werkt deze pagina door tegen je eigen repository, via het Bird-oppervlak dat hij al heeft: de MCP-server als er een verbonden is, de CLI als die geïnstalleerd en ingelogd is.

```text
I am moving an email integration from MailerSend to Bird. Route through it with me.
1. Check what you already have before setting anything up. If Bird's MCP server is connected, use its tools. If the Bird CLI is installed and signed in, use that. Either one is enough, and every step below is an action you take with whichever you have. Only if neither is present, follow https://bird.com/docs/ai/set-up-your-agent.md to set one up and sign me in. Every Bird docs page serves Markdown at its own URL with `.md` appended, so fetch that rather than the HTML.
2. Read https://bird.com/docs/guides/email/migrate/mailersend.md for the payload, suppression and webhook mapping, and https://bird.com/docs/guides/email/migrate.md for the order the steps go in.
3. Find and list my MailerSend usage in this repository before you change anything: calls to /v1/email and any SDK wrappers around them, whether I send with template_id and personalization or with html, the webhook handler and the URL it is registered at, and every domain I send from. Tell me the list before you edit anything.
4. Register each of those sending domains with Bird and give me the DNS records to publish, following https://bird.com/docs/guides/email/sending-domains.md. Leave every DNS record MailerSend uses exactly as it is: Bird's records are published alongside them and both providers authenticate side by side until I switch traffic. Publishing DNS affects mail for the whole domain, so show me the records and let me publish them.
5. Export my suppressions and import them into Bird before any production traffic goes through Bird, so my first sends do not reach addresses that already bounced or complained. MailerSend keeps five lists under /v1/suppressions: hard bounces, spam complaints, unsubscribes and the blocklist are the four to carry over. Do NOT import the On Hold list: it is a temporary 72-hour hold that MailerSend clears by itself, and importing it would suppress those addresses permanently. The Bird import takes one address per request and is idempotent, so a partial re-run is safe. https://bird.com/docs/guides/email/suppressions.md has the reason taxonomy.
6. Port the send call and the webhook handler using the mapping tables on the provider page. Both providers sign with HMAC-SHA256, so this is a re-implementation rather than new code: MailerSend sends one Signature header, Bird follows the Standard Webhooks scheme with webhook-id, webhook-timestamp and webhook-signature, and the signed string is constructed differently. Keep the constant-time comparison. https://bird.com/docs/guides/webhooks.md and https://bird.com/docs/guides/email/events.md.
7. Run my whole integration against Bird's mail sandbox before any production traffic, following https://bird.com/docs/guides/email/testing-sandbox.md. Sandbox sends run the real pipeline without reaching an inbox or touching my sending reputation.
8. Stop and ask me wherever a step needs a decision. Do not point production traffic at Bird until I have seen the sandbox results and replied with the words cut over to Bird. Retiring the MailerSend path is a separate step that comes later: ask me again and wait for me to reply with the words retire the MailerSend path. A reply that agrees without naming what it is authorising is not authorisation. Finish by telling me what is left that only a person can do.
```

## De verzendaanroep koppelen

`POST /v1/email` van MailerSend en ons [`POST /v1/email/messages`](/docs/api/reference/create-email-message) hebben dezelfde opbouw. De verschillen die code vergen zijn de taglimiet en hoe data per ontvanger wordt meegegeven.

| Wat het doet        | MailerSend                                                 | Bird                                                   |
| ------------------- | ---------------------------------------------------------- | ------------------------------------------------------ |
| Afzender            | `from` (`{email, name}`)                                   | `from`                                                 |
| Ontvangers          | `to` (max 50), `cc` / `bcc` (max 10 elk)                   | `to` / `cc` / `bcc` (arrays)                           |
| Onderwerp           | `subject` (max 998 tekens)                                 | `subject`                                              |
| Body                | `html` / `text`                                            | `html` / `text` (minstens één)                         |
| Reply-to            | `reply_to` (`{email, name}`)                               | `reply_to` (array)                                     |
| Custom headers      | `headers` (`{name, value}`, hogere abonnementen)           | `headers` (string → string object)                     |
| Filterbare labels   | `tags` (array van strings, max 5)                          | `tags`: `{name, value}`-paren                          |
| Opgeslagen template | `template_id` + `personalization`                          | `template` + `template.parameters` (zie hieronder)     |
| Bijlagen            | `attachments` (`content`, `filename`, `disposition`, `id`) | `attachments`                                          |
| Planning            | `send_at` (tot 72 uur vooruit)                             | `scheduled_at`                                         |
| Bulkvoorrang        | `precedence_bulk`                                          | (geen equivalent, zie hieronder)                       |
| Categorie           | (geen)                                                     | `category`: `marketing` (standaard) of `transactional` |
| Threading           | `in_reply_to`                                              | `headers`                                              |

Onze veldlimieten en standaardwaarden (aantallen ontvangers, tag- en metadatalimieten) staan in [E-mail verzenden](/docs/guides/email/sending-email).

Opmerkingen bij het overzetten:

- **Adressen zijn daar objecten en hier strings.** `{"email": "a@x.com", "name": "A"}` wordt `"A <a@x.com>"` of gewoon `"a@x.com"`, voor `from`, `to`, `cc`, `bcc` en `reply_to` alike.
- **`tags` zijn kale strings met een maximum van vijf. Onze tags zijn paren.** Een tag als `"welcome"` wordt `{"name": "category", "value": "welcome"}`. Kies een stabiele `name` zodat je dashboards filteren zoals je MailerSend-tagstatistieken deden.
- **`personalization` is templatedata per ontvanger.** Het is een array op basis van het e-mailadres van de ontvanger. Onze `template.parameters` is van toepassing op de hele verzending, dus een bericht waarvan de inhoud echt per ontvanger verschilt wordt één verzending per ontvanger of per stuk een [batch](/docs/guides/email/sending-bulk)-vermelding. Als je `personalization`-array voor elke ontvanger dezelfde waarden bevat, kun je die samenvoegen tot één `template.parameters`-object.
- **Round-tripcontext heeft geen MailerSend-equivalent om over te nemen.** Als je context reconstrueerde uit `tags`, gebruik dan in plaats daarvan [`metadata`](/docs/guides/email/sending-email): we sturen het mee bij elk webhook-event naast `email_id`/`recipient_id`, en het is niet beperkt tot vijf vermeldingen.
- **`precedence_bulk` heeft geen equivalent, en `category` is er geen.** `precedence_bulk` zet een `Precedence: bulk`-header, die autoresponders en afwezigheidsassistenten vraagt stil te blijven. Onze `category` bepaalt welke suppressierecords en uitschrijfvoorkeuren een bericht mogen blokkeren, en verder niets. `category` ervoor in de plaats zetten verandert het suppressiegedrag en doet niets aan autoresponders. Als je de header nodig hebt, merk dan op dat `headers` adresserings- en platformnamen weigert, maar deze header niet.
- **Custom headers en `list_unsubscribe` zijn bij MailerSend gebonden aan je abonnement.** Als je abonnement ze niet bevatte, zijn ze hier beschikbaar; zie [e-mail verzenden](/docs/guides/email/sending-email) en [categorieën](/docs/guides/email/categories) voor hoe we list-unsubscribe afhandelen bij marketingmail.

## Suppressies exporteren

MailerSend heeft vijf lijsten onder `/v1/suppressions`, één endpoint per lijst. Vier zijn permanent en neem je mee; de vijfde niet.

Exporteer en doorloop de [importlus](/docs/guides/email/migrate#3-import-suppressions):

- **Harde bounces**, bijvoorbeeld `GET https://api.mailersend.com/v1/suppressions/hard-bounces`
- **Spamklachten**
- **Uitschrijvingen**
- **Blokkeerlijst**, de adressen en patronen die je handmatig hebt toegevoegd

**Importeer de On Hold-lijst niet.** De eigen beschrijving van MailerSend is dat deze adressen bevat die "have soft bounced 5 times within 30 days", die "these emails will be blocked for 72 hours", en dat ze vervolgens "automatically removed from the list". Het is een afkoelperiode die de provider zelf opruimt, dus importeren verandert een tijdelijke blokkade in een permanente suppressie en stopt stilzwijgend het mailen naar adressen die op het punt stonden vrijgegeven te worden. Ons equivalent van dat gedrag is [uitstel](/docs/guides/email/events), dat we afhandelen op basis van actuele afleverresultaten in plaats van een geïmporteerde lijst.

De lijsttypen van MailerSend komen overeen met onze `hard_bounce`-, `complaint`- en `manual`-redenen; [Suppressies](/docs/guides/email/suppressions) bevat de volledige taxonomie.

## Webhook-events vertalen

| Resultaat              | MailerSend                                     | Bird                                             |
| ---------------------- | ---------------------------------------------- | ------------------------------------------------ |
| Geaccepteerd/verwerkt  | `activity.sent`                                | `email.accepted` → `email.processed`             |
| Afgeleverd             | `activity.delivered`                           | `email.delivered`                                |
| Tijdelijke fout        | `activity.soft_bounced` / `activity.deferred`  | `email.deferred`                                 |
| Permanente bounce      | `activity.hard_bounced`                        | `email.bounced` / `email.out_of_band_bounce`     |
| Spamklacht             | `activity.spam_complaint`                      | `email.complained`                               |
| Open                   | `activity.opened` / `activity.opened_unique`   | `email.opened`                                   |
| Klik                   | `activity.clicked` / `activity.clicked_unique` | `email.clicked`                                  |
| Uitschrijving          | `activity.unsubscribed`                        | `email.unsubscribed` / `email.list_unsubscribed` |
| Tijdelijk vastgehouden | `recipient.on_hold_added` / `..._removed`      | (geen equivalent; zie hierboven)                 |

Twee verschillen bepalen hoeveel van je handler verandert.

**Beide kanten ondertekenen met HMAC-SHA256, dus dit is een herimplementatie, geen nieuwe code.** MailerSend stuurt één `Signature`-header met een hash van de payload, berekend met het ondertekeningsgeheim van die webhook. Wij volgen het [Standard Webhooks](https://www.standardwebhooks.com)-schema, dat `webhook-id`, `webhook-timestamp` en `webhook-signature` gebruikt en een string ondertekent die is opgebouwd uit het id, de timestamp en de body, waardoor de timestamp je ook replaybescherming geeft. Behoud de constant-timevergelijking die je al hebt en vervang de opbouw; het recept staat in [Webhooks en events](/docs/guides/webhooks).

**MailerSend onderscheidt opens en clicks van hun unieke varianten. Wij niet.** `activity.opened` en `activity.opened_unique` komen beide binnen als `email.opened`, dus een handler die alleen de unieke variant telde moet nu zelf dedupliceren op `recipient_id`. Onze delivery-events zijn per ontvanger, dus een verzending naar drie ontvangers levert drie afleverresultaten op in plaats van één.

## Overschakelen

Doorloop [domeinen en DNS](/docs/guides/email/migrate#2-re-point-domains-and-dns) en de [sandbox-rooktest](/docs/guides/email/migrate#5-verify-in-the-sandbox-before-cutover) in de hoofdgids. Beide zijn provideronafhankelijk.

## Vervolgstappen

- [Verzenddomeinen](/docs/guides/email/sending-domains): registratie, verificatielevenscyclus en de DNS-records die je publiceert
- [Webhooks en events](/docs/guides/webhooks): endpointconfiguratie en Standard Webhooks-verificatie
- [Testsandbox](/docs/guides/email/testing-sandbox): rooktest van de nieuwe integratie vóór de overschakeling
- [Suppressies](/docs/guides/email/suppressions): controleer je geïmporteerde lijst en hoe wij die vanaf hier onderhouden

## Related resources

- [Getting started with email](/learn/email/getting-started-with-email) (video)
- [Email](/products/email) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=email)
