# Migreren vanuit Postmark

Deze pagina vertaalt Postmarks verzendpayload, suppressiedumps en webhooks naar Bird. Volg de [hoofdmigratiegids](/docs/guides/email/migrate) op volgorde en gebruik deze vertalingen voor stap 1, 3 en 4.

De documentatie van Postmark vermeldt dat het "does not currently support HMAC webhook signature verification" (gelezen september 2026), dus stap 4 voegt verificatie toe die je handler nu niet heeft. Lees [Webhookevents vertalen](#webhookevents-vertalen) voordat je de cutover plant.

## 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 is verbonden, de CLI als die is geïnstalleerd en ingelogd.

```text
I am moving an email integration from Postmark 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/postmark.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 Postmark usage in this repository before you change anything: calls to /email and /email/withTemplate and any SDK wrappers around them, every MessageStream name I send on, 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 Postmark 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. Postmark keeps suppressions per message stream, so dump GET /message-streams/{stream_id}/suppressions/dump once for every stream I send on rather than only the default outbound stream. 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. Postmark's docs say it does not currently support HMAC webhook signature verification, so my handler probably has no signature check and adding one is new code rather than a swap. If my registered Postmark webhook URL carries HTTP Basic credentials, take them out and tell me to rotate that pair rather than reusing it on the Bird endpoint: a URL-embedded credential should be treated as exposed. 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 Postmark path is a separate step that comes later: ask me again and wait for me to reply with the words retire the Postmark 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.
```

## Vertaal de verzendaanroep

Postmark verdeelt het verzenden over twee endpoints: `POST /email` voor een samengesteld bericht en `POST /email/withTemplate` voor een opgeslagen template. Ons [`POST /v1/email/messages`](/docs/api/reference/create-email-message) is één endpoint voor beide, met het template als veldverwijzing.

| Wat het doet        | Postmark                                                  | Bird                                                   |
| ------------------- | --------------------------------------------------------- | ------------------------------------------------------ |
| Authenticatie       | `X-Postmark-Server-Token`-header                          | `Authorization: Bearer`                                |
| Afzender            | `From`                                                    | `from`                                                 |
| Ontvangers          | `To` / `Cc` / `Bcc` (kommagescheiden, max. 50)            | `to` / `cc` / `bcc` (arrays)                           |
| Onderwerp           | `Subject`                                                 | `subject`                                              |
| Body                | `HtmlBody` / `TextBody`                                   | `html` / `text` (minstens één)                         |
| Reply-to            | `ReplyTo` (kommagescheiden)                               | `reply_to` (array)                                     |
| Aangepaste headers  | `Headers` (`Name`/`Value`-objecten)                       | `headers` (string → string-object)                     |
| Filterbaar label    | `Tag` (één per bericht)                                   | `tags`: `{name, value}`-paren                          |
| Round-tripcontext   | `Metadata`                                                | `metadata`: willekeurige JSON                          |
| Opgeslagen template | `TemplateId` / `TemplateAlias` + `TemplateModel`          | `template` + `template.parameters`                     |
| Open tracking       | `TrackOpens`                                              | `track_opens` (standaard `true`)                       |
| Kliktracking        | `TrackLinks` (`None`/`HtmlAndText`/`HtmlOnly`/`TextOnly`) | `track_clicks` (boolean, zie hieronder)                |
| Bijlagen            | `Attachments` (`Name`, `Content`, `ContentType`)          | `attachments`                                          |
| Verkeerssplitsing   | `MessageStream`                                           | (geen equivalent, zie hieronder)                       |
| Categorie           | (geen)                                                    | `category`: `marketing` (standaard) of `transactional` |
| Planning            | (geen)                                                    | `scheduled_at`                                         |

De limieten en standaardwaarden van onze velden (aantallen ontvangers, tag- en metadatalimieten) staan in [E-mail verzenden](/docs/guides/email/sending-email).

Opmerkingen bij het porten:

- **Ontvangers zijn strings in Postmark en arrays hier.** `"a@x.com, b@x.com"` wordt `["a@x.com", "b@x.com"]`. Als je code die string opbouwt door een lijst te joinen, verwijder dan de join in plaats van de lijst.
- **`Tag` is één string per bericht. Onze `tags` zijn paren, en er kunnen er meerdere zijn.** Een tag als `"welcome"` wordt `{"name": "category", "value": "welcome"}`. Kies een stabiele `name` zodat je dashboards op dezelfde manier filteren als je Postmark-tagstatistieken deden.
- **`Metadata` vertaalt direct, en we sturen het terug.** We retourneren je `metadata` (en `tags`) bij elk webhookevent samen met `email_id`/`recipient_id`, zodat je handlers je context terugkrijgen zonder een lookup.
- **Kliktracking is daar een enum en hier een boolean.** `TrackLinks: "None"` is `track_clicks: false`; de drie ingeschakelde waarden worden allemaal `track_clicks: true`, omdat we HTML- en tekstdelen niet apart tracken.
- **Templates gaan in dezelfde aanroep.** Er is geen apart template-endpoint: `TemplateId` of `TemplateAlias` wordt `template` (op ID of slug) en `TemplateModel` wordt `template.parameters`, op `POST /v1/email/messages`. Zie [verzenden met een template](/docs/guides/email/sending-email#sending-with-a-template).
- **Een message stream heeft geen equivalent, en `category` is er geen.** Een stream is een container met een eigen suppressielijst, eigen statistieken en eigen webhooks. Onze [categorie](/docs/guides/email/categories) is een per-berichtvlag met één effect: het bepaalt welke suppressierecords en uitschrijfvoorkeuren dat bericht mogen blokkeren. `category: transactional` instellen omdat een bericht uit een transactionele stream kwam, klopt meestal, maar het is een uitspraak over waarom je verstuurt, niet een port van de stream. Niets hier reproduceert per-streamstatistieken of per-streamsuppressiescoping; gebruik [tags](/docs/guides/email/sending-email#tags-vs-metadata) voor de rapportagesplitsing.

## Suppressies exporteren

Postmark bewaart suppressies **per message stream**, dus er is geen enkele accountbrede lijst om op te halen. Dump voor elke stream waarop je verstuurt de lijst en verwerk het resultaat via de [importloop](/docs/guides/email/migrate#3-import-suppressions):

- `GET /message-streams/{stream_id}/suppressions/dump`

Inventariseer eerst je streams en dump elke stream waarop je nog verstuurt. De standaard `outbound`-stream als de volledige lijst behandelen neemt de transactionele suppressies mee en mist de broadcastsuppressies, en je komt erachter doordat je mensen mailt die zich hebben afgemeld. De `SuppressionReason`-waarden zijn `HardBounce`, `SpamComplaint` en `ManualSuppression`, die vertalen naar onze `hard_bounce`-, `complaint`- en `manual`-redenen. [Suppressies](/docs/guides/email/suppressions) bevat de volledige taxonomie.

## Webhookevents vertalen

Postmark stuurt één webhooktype per event en identificeert het via het `RecordType`-veld in de payload.

| Resultaat              | Postmark                          | Bird                                             |
| ---------------------- | --------------------------------- | ------------------------------------------------ |
| Geaccepteerd/verwerkt  | (het API-antwoord)                | `email.accepted` → `email.processed`             |
| Afgeleverd             | `Delivery`                        | `email.delivered`                                |
| Tijdelijke fout        | `Bounce` met een tijdelijk `Type` | `email.deferred`                                 |
| Permanente bounce      | `Bounce` met `Type: HardBounce`   | `email.bounced` / `email.out_of_band_bounce`     |
| Spamklacht             | `SpamComplaint`                   | `email.complained`                               |
| Geblokkeerd/onderdrukt | (geen)                            | `email.rejected`                                 |
| Open                   | `Open`                            | `email.opened`                                   |
| Klik                   | `Click`                           | `email.clicked`                                  |
| Uitschrijving          | `SubscriptionChange`              | `email.unsubscribed` / `email.list_unsubscribed` |

Twee verschillen bepalen hoeveel je handler verandert.

**Verificatie is nieuwe code, geen vervanging.** De documentatie van Postmark vermeldt dat het "does not currently support HMAC webhook signature verification" (gelezen september 2026) en raadt HTTP Basic-credentials aan die in de geregistreerde URL zijn ingebed (`https://<username>:<password>@example.com/webhook`), plus de IP-ranges in je firewall. Wij ondertekenen elke aflevering volgens het [Standard Webhooks](https://www.standardwebhooks.com) HMAC-schema, dus je handler krijgt een verificatiestap die hij niet had. Het recept staat in [Webhooks en events](/docs/guides/webhooks). Doe deze stap eerst: een handler die niet-ondertekende verzoeken accepteert is het enige dat de migratie niet mag overnemen.

**Roteer de credentials in plaats van ze te hergebruiken.** Een gebruikersnaam en wachtwoord dat in een webhook-URL heeft gestaan, moet je als blootgesteld beschouwen, omdat URL's in accesslogs, configuratie-exports en een vendorconsole terechtkomen. Haal ze uit het endpoint en geef een nieuw paar uit als iets anders ze nog nodig heeft; neem het oude paar niet mee naar het Bird-endpoint, dat op handtekening authenticeert.

**Postmark rapporteert harde en zachte bounces als één `Bounce`-record met een `Type`-veld. Wij rapporteren ze als verschillende events.** Een handler die op `Type` vertakt binnen een bouncepayload, vertakt hier op de eventnaam: tijdelijke fouten komen binnen als `email.deferred` en permanente als `email.bounced`. Elk bouncetype dat Postmark onderscheidt staat in zijn Bounce API-referentie; wat voor de port telt is aan welke kant van die splitsing elk type valt.

Onze afleverevents zijn per ontvanger (`recipient_id` naast `email_id`), dus een verzending aan drie ontvangers levert drie afleverresultaten op in plaats van één.

## Cutover

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.

## Volgende stappen

- [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 de nieuwe integratie vóór de cutover
- [Suppressies](/docs/guides/email/suppressions): controleer je geïmporteerde lijst en hoe we 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)
