# Migracja z Postmark

Ta strona mapuje payload wysyłki, eksporty supresji i webhooki Postmark na Bird. Postępuj zgodnie z [głównym przewodnikiem migracji](/docs/guides/email/migrate) po kolei i użyj tych mapowań w krokach 1, 3 i 4.

Dokumentacja Postmark stwierdza, że "does not currently support HMAC webhook signature verification" (odczytano we wrześniu 2026), więc krok 4 dodaje weryfikację, której Twój handler dziś nie ma. Przeczytaj [Tłumaczenie zdarzeń webhooków](#tłumaczenie-zdarzeń-webhooków), zanim zaplanujesz przełączenie.

## Przekaż to swojemu agentowi

Wklej to do Claude Code, Cursor lub Codex. Agent przechodzi tę stronę na tle Twojego repozytorium, korzystając z dowolnej powierzchni Bird, którą już ma: serwera MCP, jeśli jest podłączony, lub CLI, jeśli jest zainstalowany i zalogowany.

```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.
```

## Zmapuj wywołanie wysyłki

Postmark dzieli wysyłkę na dwa endpointy: `POST /email` dla skomponowanej wiadomości i `POST /email/withTemplate` dla zapisanego szablonu. Nasz [`POST /v1/email/messages`](/docs/api/reference/create-email-message) to jeden endpoint dla obu, z szablonem wskazanym w polu.

| Co robi                 | Postmark                                                  | Bird                                                    |
| ----------------------- | --------------------------------------------------------- | ------------------------------------------------------- |
| Autoryzacja             | Nagłówek `X-Postmark-Server-Token`                        | `Authorization: Bearer`                                 |
| Nadawca                 | `From`                                                    | `from`                                                  |
| Odbiorcy                | `To` / `Cc` / `Bcc` (rozdzielone przecinkami, maks. 50)   | `to` / `cc` / `bcc` (tablice)                           |
| Temat                   | `Subject`                                                 | `subject`                                               |
| Treść                   | `HtmlBody` / `TextBody`                                   | `html` / `text` (co najmniej jedno)                     |
| Reply-to                | `ReplyTo` (rozdzielone przecinkami)                       | `reply_to` (tablica)                                    |
| Własne nagłówki         | `Headers` (obiekty `Name`/`Value`)                        | `headers` (obiekt string → string)                      |
| Etykieta do filtrowania | `Tag` (jedna na wiadomość)                                | `tags`: pary `{name, value}`                            |
| Kontekst zwrotny        | `Metadata`                                                | `metadata`: dowolny JSON                                |
| Zapisany szablon        | `TemplateId` / `TemplateAlias` + `TemplateModel`          | `template` + `template.parameters`                      |
| Śledzenie otwarć        | `TrackOpens`                                              | `track_opens` (domyślnie `true`)                        |
| Śledzenie kliknięć      | `TrackLinks` (`None`/`HtmlAndText`/`HtmlOnly`/`TextOnly`) | `track_clicks` (boolean, patrz niżej)                   |
| Załączniki              | `Attachments` (`Name`, `Content`, `ContentType`)          | `attachments`                                           |
| Separacja ruchu         | `MessageStream`                                           | (brak odpowiednika, patrz niżej)                        |
| Kategoria               | (brak)                                                    | `category`: `marketing` (domyślnie) lub `transactional` |
| Planowanie              | (brak)                                                    | `scheduled_at`                                          |

Nasze limity pól i wartości domyślne (liczba odbiorców, limity tagów i metadanych) znajdziesz w [Wysyłanie e-maili](/docs/guides/email/sending-email).

Uwagi dotyczące portowania:

- **Odbiorcy to stringi w Postmark, a tu tablice.** `"a@x.com, b@x.com"` staje się `["a@x.com", "b@x.com"]`. Jeśli Twój kod buduje ten string przez łączenie listy, usuń łączenie, a nie listę.
- **`Tag` to jeden string na wiadomość. Nasze `tags` to pary i może ich być kilka.** Tag taki jak `"welcome"` staje się `{"name": "category", "value": "welcome"}`. Wybierz stabilny `name`, żeby Twoje dashboardy filtrowały tak, jak robiły to statystyki tagów w Postmark.
- **`Metadata` przenosi się bezpośrednio i zwracamy go z powrotem.** Zwracamy Twój `metadata` (i `tags`) w każdym zdarzeniu webhooka obok `email_id`/`recipient_id`, więc Twoje handlery odzyskują kontekst bez dodatkowego zapytania.
- **Śledzenie kliknięć to tam enum, a tu boolean.** `TrackLinks: "None"` to `track_clicks: false`; trzy wartości włączające stają się `track_clicks: true`, ponieważ nie śledzimy osobno części HTML i tekstowych.
- **Szablony wchodzą do tego samego wywołania.** Nie ma osobnego endpointu szablonów: `TemplateId` lub `TemplateAlias` staje się `template` (po ID lub slugu), a `TemplateModel` staje się `template.parameters`, w `POST /v1/email/messages`. Zobacz [wysyłanie z szablonem](/docs/guides/email/sending-email#sending-with-a-template).
- **Strumień wiadomości nie ma odpowiednika, a `category` nim nie jest.** Strumień to kontener z własną listą supresji, własnymi statystykami i własnymi webhookami. Nasza [kategoria](/docs/guides/email/categories) to flaga per wiadomość z jednym efektem: decyduje, które rekordy supresji i preferencje wypisania mogą zablokować tę wiadomość. Ustawienie `category: transactional`, bo wiadomość pochodziła ze strumienia transakcyjnego, zwykle jest prawidłowe, ale to deklaracja o celu wysyłki, a nie port strumienia. Nic tu nie odtwarza statystyk per strumień ani zakresów supresji per strumień; do podziału raportowania użyj [tagów](/docs/guides/email/sending-email#tags-vs-metadata).

## Eksportuj supresje

Postmark przechowuje supresje **per strumień wiadomości**, więc nie ma jednej listy obejmującej całe konto. Dla każdego strumienia, na którym wysyłasz, wyeksportuj go i przepuść wynik przez [pętlę importu](/docs/guides/email/migrate#3-import-suppressions):

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

Najpierw wylicz swoje strumienie i wyeksportuj każdy, na którym nadal wysyłasz. Potraktowanie domyślnego strumienia `outbound` jako jedynej listy przeniesie supresje transakcyjne, a pominie broadcastowe, a dowiesz się o tym dopiero wysyłając do osób, które się wypisały. Wartości `SuppressionReason` to `HardBounce`, `SpamComplaint` i `ManualSuppression`, które mapują się na nasze powody `hard_bounce`, `complaint` i `manual`. Pełną taksonomię znajdziesz w [Supresje](/docs/guides/email/suppressions).

## Tłumaczenie zdarzeń webhooków

Postmark wysyła jeden typ webhooka na zdarzenie i identyfikuje go polem `RecordType` w payloadzie.

| Wynik                      | Postmark                       | Bird                                             |
| -------------------------- | ------------------------------ | ------------------------------------------------ |
| Zaakceptowano/przetworzone | (odpowiedź API)                | `email.accepted` → `email.processed`             |
| Dostarczono                | `Delivery`                     | `email.delivered`                                |
| Tymczasowa awaria          | `Bounce` z przejściowym `Type` | `email.deferred`                                 |
| Trwałe odrzucenie          | `Bounce` z `Type: HardBounce`  | `email.bounced` / `email.out_of_band_bounce`     |
| Skarga na spam             | `SpamComplaint`                | `email.complained`                               |
| Zablokowano/wstrzymano     | (brak)                         | `email.rejected`                                 |
| Otwarcie                   | `Open`                         | `email.opened`                                   |
| Kliknięcie                 | `Click`                        | `email.clicked`                                  |
| Wypisanie                  | `SubscriptionChange`           | `email.unsubscribed` / `email.list_unsubscribed` |

Dwie różnice decydują o tym, jak bardzo zmieni się Twój handler.

**Weryfikacja to nowy kod, a nie podmiana.** Dokumentacja Postmark stwierdza, że "does not currently support HMAC webhook signature verification" (odczytano we wrześniu 2026), i zaleca poświadczenia HTTP Basic osadzone w zarejestrowanym URL (`https://<username>:<password>@example.com/webhook`) oraz zakresy IP w Twoim firewallu. My podpisujemy każde dostarczenie zgodnie ze schematem HMAC [Standard Webhooks](https://www.standardwebhooks.com), więc Twój handler zyskuje krok weryfikacji, którego wcześniej nie miał. Przepis znajdziesz w [Webhooki i zdarzenia](/docs/guides/webhooks). Zrób ten krok jako pierwszy: handler akceptujący niepodpisane żądania to jedyna rzecz, której migracja nie powinna przenosić.

**Zrotuj poświadczenia zamiast ich ponownego użycia.** Nazwę użytkownika i hasło, które żyły w URL webhooka, traktuj jako ujawnione, ponieważ URL-e trafiają do logów dostępu, eksportów konfiguracji i konsoli dostawcy. Usuń je z endpointu i wystaw nową parę, jeśli cokolwiek innego ich jeszcze potrzebuje; nie przenoś starej pary na endpoint Bird, który uwierzytelnia podpisem.

**Postmark raportuje twarde i miękkie odrzucenia jako jeden rekord `Bounce` z polem `Type`. My raportujemy je jako różne zdarzenia.** Handler, który rozgałęzia się na `Type` wewnątrz payloadu odrzuceń, tu rozgałęzia się na nazwie zdarzenia: przejściowe awarie przychodzą jako `email.deferred`, a trwałe jako `email.bounced`. Wszystkie typy odrzuceń, które Postmark rozróżnia, znajdziesz w jego referencji Bounce API; dla portowania liczy się to, po której stronie tego podziału każdy z nich się znajduje.

Nasze zdarzenia dostarczenia są zakresowe per odbiorca (`recipient_id` obok `email_id`), więc wysyłka do trzech odbiorców generuje trzy wyniki dostarczenia, a nie jeden.

## Przełączenie

Przejdź przez [domeny i DNS](/docs/guides/email/migrate#2-re-point-domains-and-dns) oraz [test dymny w sandboxie](/docs/guides/email/migrate#5-verify-in-the-sandbox-before-cutover) w głównym przewodniku. Oba są niezależne od dostawcy.

## Następne kroki

- [Domeny wysyłkowe](/docs/guides/email/sending-domains): rejestracja, cykl życia weryfikacji i rekordy DNS, które publikujesz
- [Webhooki i zdarzenia](/docs/guides/webhooks): konfiguracja endpointu i weryfikacja Standard Webhooks
- [Sandbox testowy](/docs/guides/email/testing-sandbox): przetestuj nową integrację przed przełączeniem
- [Supresje](/docs/guides/email/suppressions): potwierdź zaimportowaną listę i sposób, w jaki ją utrzymujemy od tej pory

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