email · side by side
Postmark vs Resend for a Next.js app
Both fill the email slot, so a generated repo carries one or the other, never both. Every line below is read out of the two manifests.
Short answer
Pick Postmark if
Products where the transactional mail has to arrive now: sign-in links, password resets, receipts. Also teams that send some marketing and want it kept on separate infrastructure from the mail that must not fail.
Pick Resend if
Product teams sending transactional mail: magic links, receipts, notifications. Templates are React components, reviewed in a pull request, not pasted into a vendor's WYSIWYG editor.
Side by side
Price, obligations, and the surface each one adds. No row is written by hand. This is manifest.yaml, rendered.
| From the manifest | Option APostmark | Option BResend |
|---|---|---|
| In one line | Postmark Transactional email with separate broadcast streams and 45 days of history. | Resend Transactional email with React Email templates you review in a pull request. |
| Pricing | Postmark Free developer plan: 100 emails a month, no expiry. Paid plans start at $15/month for 10,000 emails. Every plan keeps 45 days of message history by default. Dedicated IPs (from $50/month) and longer retention are add-ons. | Resend Free: 3,000 emails a month (100 a day) on up to 3 domains. Pro is $20/month for 50,000 emails and 10 domains. Every self-serve plan keeps 30 days of data. Dedicated IPs are a $30/month add-on on Scale. |
| Best for | Postmark Products where the transactional mail has to arrive now: sign-in links, password resets, receipts. Also teams that send some marketing and want it kept on separate infrastructure from the mail that must not fail. | Resend Product teams sending transactional mail: magic links, receipts, notifications. Templates are React components, reviewed in a pull request, not pasted into a vendor's WYSIWYG editor. |
| Trade-offsVerbatim from the manifest | Postmark
| Resend
|
| Required companionsAdded for you, with a reason | Postmark Nothing. It stands on its own. | Resend Nothing. It stands on its own. |
| Recommended alongsideSuggested, never added for you | Postmark
| Resend
|
| Env vars you will manageEvery one documented in docs/onboard.md | Postmark 8 variables · 2 required
| Resend 5 variables · 2 required
|
| Dependencies added | Postmark
| Resend
|
| MCP serversWritten into .mcp.json | Postmark
| Resend None. No extra agent tools from this one. |
| Footprint in your repo | Postmark 29 files, plus 3 injections into shared stack files | Resend 26 files, plus 3 injections into shared stack files |
What changes in your repo
The paths each battery contributes, diffed. A path in the third list is written by both, so swapping rewrites that file rather than adding one.
Only with Postmark (5)
scripts/1 file
email/1 file
- suppressions.ts
src/4 files
app/1 file
api/1 file
webhooks/1 file
postmark/1 file
- route.ts
lib/3 files
email/3 files
templates/1 file
- layout.tsx
- postmark.ts
- webhook-auth.ts
Only with Resend (2)
src/2 files
app/1 file
api/1 file
webhooks/1 file
resend/1 file
- route.ts
lib/1 file
email/1 file
- resend.ts
Same path, different implementation (24)
scripts/2 files
email/2 files
- render-samples.ts
- send-test.ts
src/12 files
lib/12 files
email/12 files
templates/6 files
- magic-link.tsx
- receipt.tsx
- reset-password.tsx
- theme.ts
- verify-email.tsx
- welcome.tsx
- address.ts
- index.ts
- outbox.ts
- retry.ts
- suppression.ts
- tags.ts
tests/2 files
unit/2 files
- email-outbox.test.ts
- email.test.ts
variants/8 files
errors-none/1 file
src/1 file
lib/1 file
email/1 file
- observability.ts
errors-sentry/1 file
src/1 file
lib/1 file
email/1 file
- observability.ts
orm-drizzle/3 files
slots/1 file
- db-schema.ts
src/2 files
db/1 file
- email-schema.ts
lib/1 file
email/1 file
- store.ts
orm-none/1 file
src/1 file
lib/1 file
email/1 file
- store.ts
orm-prisma/2 files
slots/1 file
- prisma-models.prisma
src/1 file
lib/1 file
email/1 file
- store.ts
Shared stack files Postmark injects into
- env-required
- legal-processors
- verify-checks
Shared stack files Resend injects into
- env-required
- legal-processors
- verify-checks
Postmark in your .env.local
# required
EMAIL_FROM=my-app <hello@mail.example.com>
POSTMARK_SERVER_TOKEN=POSTMARK_API_TEST
# optional
EMAIL_OUTBOX_DIR=
POSTMARK_BROADCAST_STREAM=broadcast
POSTMARK_MESSAGE_STREAM=outbound
POSTMARK_WEBHOOK_PASSWORD=replace_with_a_long_random_string
POSTMARK_WEBHOOK_USERNAME=postmark
REPLY_TO=support@example.com
Resend in your .env.local
# required
EMAIL_FROM=my-app <hello@mail.example.com>
RESEND_API_KEY=re_replace_me
# optional
EMAIL_OUTBOX_DIR=
REPLY_TO=support@example.com
RESEND_WEBHOOK_SECRET=whsec_replace_me
What changes for your agents
Each battery ships rules, skills, subagents and hooks that an agent loads before it touches the code that battery owns. Picking one is also picking how your agents behave in src/lib/email/**.
Postmark
2
Skills
1
Rules
6
Solution docs
1
MCP servers
Rules (1)
One send path, the right message stream, and respect for inactive recipients
src/lib/email/** · src/app/api/webhooks/postmark/** · src/db/email-schema.ts · scripts/email/** · src/lib/auth/** · src/lib/billing/**
Skills (2)
/add-email-template
Add a React Email template on the shared layout, pick its message stream, wire it into a sendEmail call, and prove it lands in a real inbox.
/test-email
Prove Postmark sending works. Check the token and stream, send one of each template, read the activity feed, check suppressions, and test the basic-auth webhook with a fake bounce.
Subagents and hooks
None of its own. The foundation agents and guard hooks still ship.
Resend
2
Skills
1
Rules
5
Solution docs
Rules (1)
Every email goes through sendEmail, from a verified domain, with a reply-to
src/lib/email/** · src/app/api/webhooks/resend/** · src/lib/auth/** · src/lib/billing/**
Skills (2)
/add-email-template
Add a React Email template, preview it, wire it into a send, and check it renders and lands in a real inbox.
/preview-and-test-email
Diagnose an email problem (not sending, landing in spam, rendering wrong) in the order that finds the cause fastest.
Subagents and hooks
None of its own. The foundation agents and guard hooks still ship.
What each one already knows
Solution docs land in docs/solutions/ in your repo and are published here, so you can read the failure modes before you commit.
Postmark (6)
- Postmark bounce and spam complaint webhooks, secured without a signaturePostmark does not sign webhooks. Protect the endpoint with basic auth, act only on deactivating bounces, and make every write an upsert.docs/solutions/postmark/bounces-complaints-and-webhook-auth.md
- Postmark DKIM and custom Return-Path, and why DMARC fails without themPostmark needs two DNS records per domain. DKIM signs the mail. The pm-bounces Return-Path CNAME makes SPF align. Skip one and DMARC alignment rests on the other.docs/solutions/postmark/dkim-return-path-dns.md
- Postmark error 406: you tried to send to a recipient that has been marked as inactiveA 406 means Postmark deactivated the address after a hard bounce, complaint or manual block. Do not retry it. Surface it, and reactivate only on the owner's request.docs/solutions/postmark/inactive-recipient-406.md
- Postmark message streams, transactional vs broadcast, and why the split protects your login emailsPostmark sends transactional and broadcast mail through separate streams with separate reputations and suppression lists. Route every send on purpose, and never put marketing on the transactional stream.docs/solutions/postmark/message-streams-transactional-vs-broadcast.md
- Postmark server-side templates vs React Email rendered in your appPostmark can store templates and fill them with Mustachio. Rendering React Email in your app keeps templates in the repo, typed and reviewed. How to choose, and what each costs.docs/solutions/postmark/postmark-templates-vs-react-email.md
- Testing Postmark without emailing anyone, POSTMARK_API_TEST vs sandbox servers vs bounce-testingPostmark has three ways to send without reaching a real inbox. Each tests something different. Pick by what you need to prove.docs/solutions/postmark/testing-with-postmark-api-test-and-sandbox.md
Resend (5)
- Sending a lot of email without hitting the rate limit or the spam folderResend allows a couple of requests a second by default. Use the batch endpoint, add backoff for 429s, keep one idempotency key per recipient, and never batch a magic link.docs/solutions/resend/batching-and-rate-limits.md
- Bounces and spam complaints: listen, or lose the inbox for everyoneA hard bounce means the mailbox is gone. Keep sending and mailbox providers downgrade every message from your domain. Wire the webhook, suppress permanently, and never suppress on a soft bounce.docs/solutions/resend/bounces-complaints-and-webhooks.md
- SPF, DKIM and DMARC: what each record does and why your mail needs all threeThree DNS records decide whether a mailbox provider treats your email as authentic. Here is what each one proves, how to set them up on a subdomain, and how to read a failure.docs/solutions/resend/domain-verification-spf-dkim-dmarc.md
- Magic links that actually arrive, and survive the scanner that clicks them firstA sign-in link is the highest-stakes email you send. It has to land in seconds, work once, and survive corporate mail scanners that follow every URL before the human does.docs/solutions/resend/magic-link-deliverability.md
- Previewing React Email templates locally, and what the preview cannot tell youReact Email's preview server renders your templates with realistic props and hot reload. Here is how to set it up, what to check, and the four failure modes only a real client will show you.docs/solutions/resend/previewing-templates-locally.md
Which one to pick
From meta.bestFor and meta.tradeoffs. If a claim is not in the manifest, it is not on this page.
Pick Postmark when
Products where the transactional mail has to arrive now: sign-in links, password resets, receipts. Also teams that send some marketing and want it kept on separate infrastructure from the mail that must not fail.
And accept that(5)
- Transactional first. Broadcast streams exist and handle unsubscribes, but there is no segmentation, no drip builder and no marketing CRM.
- Postmark does not sign webhooks. The endpoint is protected by basic auth over HTTPS, which works but puts the credential in the webhook config.
- No React rendering on Postmark's side. Templates are rendered to HTML and text in the app before sending, so the preview is exactly what ships.
- No idempotency key on the API. This battery sets a deterministic Message-ID and stores the key as metadata instead.
- New accounts go through a short approval before they can mail outside their own domain. Plan a day for it before launch.
Pick Resend when
Product teams sending transactional mail: magic links, receipts, notifications. Templates are React components, reviewed in a pull request, not pasted into a vendor's WYSIWYG editor.
And accept that(5)
- Transactional first. There is a broadcast product, but if you need segmentation, drip campaigns and a marketing CRM, this is not that tool.
- Shared IPs by default. Dedicated IPs are a paid add-on on the Scale plan, so on the shared pool your own sending hygiene matters more.
- Data is kept 30 days. If you need to prove what you sent someone six months ago, log the message id at send time. The repo does.
- React Email is the headline feature and a real one: templates are components, previewable locally, diffable in review. It also means your email templates are part of your build.
- Fewer knobs than a full ESP: no server-side template variables, no A/B testing, limited routing rules. For transactional mail that is a simpler model, not a gap.
Questions people actually ask
- Should I choose Postmark or Resend?
- Postmark is best for products where the transactional mail has to arrive now: sign-in links, password resets, receipts. Also teams that send some marketing and want it kept on separate infrastructure from the mail that must not fail. Resend is best for product teams sending transactional mail: magic links, receipts, notifications. Templates are React components, reviewed in a pull request, not pasted into a vendor's WYSIWYG editor. Both fill the email slot, so a generated repo carries one or the other, never both.
- How much do Postmark and Resend cost?
- Postmark: Free developer plan: 100 emails a month, no expiry. Paid plans start at $15/month for 10,000 emails. Every plan keeps 45 days of message history by default. Dedicated IPs (from $50/month) and longer retention are add-ons. Resend: Free: 3,000 emails a month (100 a day) on up to 3 domains. Pro is $20/month for 50,000 emails and 10 domains. Every self-serve plan keeps 30 days of data. Dedicated IPs are a $30/month add-on on Scale.
- What changes in my repo if I switch from Postmark to Resend?
- Postmark writes 29 files, 8 environment variables and 4 dependencies, and installs 1 path-scoped rule, 2 skills and 6 solution docs. Resend writes 26 files, 5 environment variables and 5 dependencies, and installs 1 path-scoped rule, 2 skills and 5 solution docs.
Decide once, then build the repo that already knows the decision.
Either way you get that choice’s rules, skills and solution docs installed, plus the guard hooks, an onboarding doc for exactly these env vars, and the Compound Engineering loop. Free and MIT.