Resend → Postmark
Move Resend sending to Postmark by verifying Sender Signatures or domains, creating Servers and Message Streams, and importing Resend’s team-wide suppressions into each stream. React Email can keep rendering HTML. Postmark Templates are optional. SMTP is available on Postmark if you need it.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
Resend
- A simple API, React Email, and clear domain setup make transactional sending approachable for product teams
- Audience, campaigns, automations, and reporting live in one publishing system
- Message streams, native template languages, and long IP history from older providers do not transfer in
- Automation logic, analytics history, and deliverability settings are not very portable
Postmark
- Focused transactional delivery, clear message streams, excellent diagnostics, and support simplify reliable sending
- Audience, campaigns, automations, and reporting live in one publishing system
- Marketing automation and broad multichannel features are intentionally outside its core scope
- Automation logic, analytics history, and deliverability settings are not very portable
Postmark: Focused transactional delivery, clear message streams, excellent diagnostics, and support simplify reliable sending. This removes a major source-side concern: Message streams, native template languages, and long IP history from older providers do not transfer in.
What you lose: A simple API, React Email, and clear domain setup make transactional sending approachable for product teams. What you inherit: Marketing automation and broad multichannel features are intentionally outside its core scope.
Jump to a section
Know the shape of the move.
This timeline assumes
- You send through the Resend API today. If you also use Resend SMTP, test Postmark SMTP separately.
- You can add Postmark DKIM and Return-Path records beside Resend DNS until cutover.
- Resend stays able to send until Postmark completes one production cycle.
- The site already documents Resend → SendGrid and Postmark → Resend as nearby routes.
- You export Resend suppressions before the first Postmark production send.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Domains and From addresses | manual | critical | Postmark requires a Sender Signature per address or Domain Verification with DKIM. Resend domains do not transfer. | Add signatures or verify the domain. Send a test from each From address. |
| Message Streams | manual | critical | Postmark’s guide says transactional and broadcast traffic never mix, including IP ranges. Resend has no stream object. You must pick a stream on every send. | Create transactional and broadcast streams. Set MessageStream on each request. |
| API field names | manual | critical | Postmark uses X-Postmark-Server-Token and PascalCase. Resend uses Bearer auth and snake_case. Postmark To, Cc, and Bcc are comma-separated strings. Resend uses arrays. | Rewrite the client. Do not post Resend JSON to Postmark. |
| Recipient and batch limits | partial | high | Postmark’s guide says 50 recipients per message and 500 messages per batch. It says Resend allows 50 recipients and 100 batch messages. Postmark’s outbound size limit is 10 MB including attachments. | Split batches that assumed Resend’s 100-message cap if you now pack 500, and watch the 10 MB cap. |
| Templates | partial | high | Postmark offers Mustachio templates. You can keep rendering HTML in the app, including React Email, and pass HtmlBody. | Pick one: keep app-rendered HTML, or port templates into Postmark. |
| Suppressions | partial | critical | Resend suppressions are team-wide across every domain. Postmark suppressions are per stream. A Resend bounce list is not a Postmark import. | Download the Resend CSV. Load HardBounce, SpamComplaint, and ManualSuppression rows onto each stream that will send to those people. |
| SMTP | partial | high | Postmark’s guide says Resend is API-only and that Postmark supports SMTP. Resend’s own Postmark migrate page documents SMTP. If you used Resend SMTP, confirm Postmark’s username-as-token rules and that port 465 is not supported on Postmark. | Rehearse Postmark SMTP or move that path to the API. |
| Inbound email | manual | high | Postmark inbound can post the full body and base64 attachments on the webhook. If you used Resend receiving, the consumer changes. | Point inbound MX at Postmark and test a full MIME message. |
| Idempotency keys | lost | medium | Postmark’s guide says it does not currently support idempotency keys. Resend does on send and batch. | Make retries safe in your app. Do not assume Postmark will drop a duplicate POST. |
| Webhook event set | partial | high | Event names change. Postmark documents sent and delivery-delay gaps on its own side in Resend’s guide. Map what your consumer actually reads. | Pilot Delivery, Bounce, Open, Click, and SpamComplaint. |
| Dedicated IPs and warm-up | lost | critical | Resend IP reputation does not become a Postmark IP. Postmark says dedicated IPs are optional and have eligibility rules. | Warm the new stream. Do not jump to full volume on day one. |
| Retention | lost | medium | Resend’s migrate page lists 30 days of Resend data and 45 days of Postmark data. History still does not import. | Export Resend email logs you still need. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| Resend domain | Postmark Sender Signature or verified domain | manual | DKIM required for domain verification. |
| RESEND_API_KEY | Server API token in X-Postmark-Server-Token | manual | Rotate. |
| html / text / to array | HtmlBody / TextBody / To string | transform | PascalCase. |
| React Email HTML | HtmlBody, or a Postmark Template | transform | HTML still works. |
| Team suppression CSV | Per-stream suppression list | transform | Load every stream that will send. |
| Resend webhook | Postmark webhook | manual | Map event names. |
| Broadcast send | Broadcast Message Stream | manual | Set MessageStream. |
| Transactional send | Transactional Message Stream | manual | Do not mix streams. |
| Idempotency-Key | No destination | unsupported | Handle duplicates in the app. |
Make the move recoverable.
Create the source-of-truth backup
Keep Resend keys, suppressions, and DNS so you can send again.
- List domains, From addresses, templates, webhooks, and inbound.
- Download the Resend suppressions CSV.
- Archive React Email or template source.
- Record current Resend DNS.
- Keep a Resend key that can still send.
Proof to capture: A manifest lists domains, the CSV hash, and the Resend key owner.
Postmark client
Send PascalCase messages with a stream ID.
- Set X-Postmark-Server-Token.
- Map html to HtmlBody and join to addresses with commas.
- Set MessageStream on every call.
- Remove Idempotency-Key or replace it with your own dedupe.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Suppression fan-out
Copy the Resend team list onto each Postmark stream.
- Export Resend suppressions.
- Map origin bounce to HardBounce, complaint to SpamComplaint, manual to ManualSuppression.
- Apply the list to transactional and broadcast streams.
- Test a known bounce on each stream.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
You omit MessageStream
A send fails or lands on the wrong stream.
- Consequence
- Transactional mail shares broadcast reputation, or the API rejects the message.
- Mitigation
- Set the stream in one shared client. Fail closed if it is missing.
Stop if: A production send has no approved stream.
Suppressions land on only one stream
Broadcast imported the CSV. Transactional did not.
- Consequence
- Password resets go to addresses Resend already suppressed.
- Mitigation
- Test a known bounce on every stream.
Stop if: Any stream accepts a suppressed address.
Retries double-send
The app still retries like Resend idempotency is present.
- Consequence
- Users get the same message twice.
- Mitigation
- Dedupe in the app. Watch canary inboxes during the first retry storm.
Stop if: A canary receives two copies from one logical send.
Do the work in this order.
- Days 1–3 · inventory
Inventory Resend
5–9 hours active2–3 days elapsedOwner review waiting- List domains, send paths, and inbound.
- Decide stream layout.
Depends on: Resend and Postmark admin access
Stop / go checkpointExport?
Go when: Every send path has a stream.
Stop when: A production From address is unknown.
- Days 3–5 · backup
Export suppressions
3–6 hours active1–2 days elapsedCSV download waiting- Download the Resend CSV.
- Archive templates and DNS.
Depends on: inventory
Stop / go checkpointBuild Postmark?
Go when: The CSV hashes.
Stop when: The export is empty and you know you have bounces.
- Days 5–12 · pilot
Verify and rewrite
10–22 hours active5–7 days elapsedDNS and inbox tests waiting- Verify Postmark domains.
- Rewrite the client.
- Load suppressions on staging streams.
- Pilot inbound if needed.
Depends on: backup
Stop / go checkpointCut over?
Go when: Canary, stream routing, and skip-on-suppression pass.
Stop when: A required send path still posts Resend JSON.
- Days 12–16 · cutover
Switch senders
3–8 hours active1–3 days elapsedQueue drain waiting- Load production suppressions on each stream.
- Point the app at Postmark.
- Drain Resend jobs.
Depends on: pilot
Stop / go checkpointPostmark owns sending?
Go when: No Resend production send remains.
Stop when: A duplicate or a suppressed send appears.
- Days 16–18 · observe
Watch deliverability
3–6 hours active3–5 days elapsedInbox signals waiting- Watch bounces and complaints.
- Keep Resend ready.
Depends on: cutover
Stop / go checkpointClose rollback?
Go when: One send cycle is clean.
Stop when: Rates jump.
Cut over with a way back.
Cutover
Make Postmark the only production sender after suppressions exist on every live stream.
- Import the Resend CSV onto each production stream.
- Switch the application to the Postmark token.
- Drain Resend queues.
- Send transactional and broadcast canaries.
- Confirm a known bounce is skipped on each stream.
Proof to capture: Postmark sends production mail. Resend is idle. Known suppressions are skipped.
Rollback
Return sending to Resend without losing Postmark-era suppressions you still need.
- Stop Postmark production sends.
- Restore the Resend key and DNS.
- Export new Postmark suppressions.
- Import reviewed rows into Resend.
- Send a canary through Resend.
Proof to capture: Resend is the only production sender again.
- Suppressed address received mail
- Missing MessageStream
- Duplicate send
- Inbound parse failure
- Bounce or complaint spike
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Postmark domain or signature | Send from each production From address. | Postmark accepts the message. | Signature page and received header. | |
V-02Blocking | MessageStream set | Inspect production requests. | Transactional and broadcast use the approved IDs. | Request log. | |
V-03Blocking | Each stream skips a known bounce | Send the same suppressed address on every live stream. | Every stream refuses delivery. | Suppression dump. | |
V-04Blocking | Field names | Inspect a canary. | PascalCase body. Token header, not Bearer. | Request log. | |
V-05Blocking | Event consumer | Trigger Delivery and Bounce. | One mapped event each. | Consumer log. | |
V-06Blocking | No duplicate canary | Retry a send the way production does. | One delivered message. | Inbox and message ID. | |
V-07Blocking | Resend idle | Watch Resend Emails after cutover. | No new production sends. | Emails screenshot. | |
V-08Blocking | Resend still sends | Keep a rollback key and send a staging message. | Resend still accepts mail. | Rollback send. |
Remove the scaffolding safely.
Safe after: One full send cycle, stable rates, and seven days without a rollback trigger.
- Archive the Resend CSV and Postmark stream dumps.
- Revoke the Resend production key.
- Remove Resend DNS after mail no longer uses it.
- Keep Resend readable through the retention window.
- Document stream IDs and token rotation.
- Schedule the next suppression review.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- Postmark: migrate from ResendAccessed 2026-10-31
- Resend: email suppressionsAccessed 2026-10-31
- Postmark: getting started and domain verificationAccessed 2026-10-31
- Postmark: Suppressions APIAccessed 2026-10-31