Skip to content

Migrate from Postmark to PostStack

Postmark-grade deliverability on EU infrastructure. €5 instead of $15 to get started.

Postmark is well-respected for transactional deliverability — they were one of the first providers to separate transactional and broadcast traffic into message streams to protect inbox placement. PostStack shares the same hygiene defaults: fast bounce and complaint suppression, one-click unsubscribe, and strict DKIM/SPF/DMARC alignment. The two send APIs are similar in shape; the most visible difference is field casing (Postmark uses PascalCase, PostStack uses lowercase snake_case) and the broader product surface — PostStack includes contacts and segments, IMAP mailboxes, a visual email builder, and DMARC aggregate reports that Postmark does not.

Why switch from Postmark?

  • €5/month for 10,000 emails, roughly 62% cheaper than Postmark at that volume
  • Savings of roughly 62–76% at every paid volume in the table below
  • IMAP and POP3 mailboxes, a visual email builder, and DMARC aggregate reports, none of which Postmark ships
  • European infrastructure in Helsinki, Finland, not subject to the US CLOUD Act

Price comparison

Competitor prices are public list prices (USD) verified September 2026; PostStack prices are in EUR. Figures are approximate after currency conversion — check each provider's pricing page for current rates.

VolumePostmarkPostStackSavings
3,000/mo~$15/mo€0 (free)100%
10,000/mo~$15/mo€5/mo~62%
50,000/mo~$66/mo€15/mo~74%
100,000/mo~$126/mo€30/mo~73%
500,000/mo~$606/mo€130/mo~75%
1,000,000/mo~$1,206/mo€255/mo~76%

Code migration

Before (Postmark)

typescript
import * as postmark from 'postmark';
const client = new postmark.ServerClient(process.env.POSTMARK_API_KEY!);

await client.sendEmail({
  From: 'hello@yourdomain.com',
  To: 'user@example.com',
  Subject: 'Hello!',
  HtmlBody: '<h1>Welcome!</h1>',
});

After (PostStack)

typescript
import { PostStack } from '@poststack.dev/sdk';
const poststack = new PostStack('sk_live_...');

await poststack.emails.send({
  from: 'hello@yourdomain.com',
  to: ['user@example.com'],
  subject: 'Hello!',
  html: '<h1>Welcome!</h1>',
});

Feature comparison

FeaturePostmarkPostStack
REST APIYesYes
TypeScript SDKYesYes
WebhooksYesYes
Email TemplatesYesYes
BroadcastsYesYes
Contact ManagementLimitedYes
Open/Click TrackingYesYes
SMTP RelayYesYes
Inbound Email + WebhooksYesYes
IMAP/POP3 MailboxesNoYes
Visual Email BuilderNoYes
DMARC Aggregate ReportsNoYes
Workflow AutomationNoYes
Embeddable Signup FormsNoYes
Contact SegmentationNoYes
EU Data ResidencyNoYes

API mapping

Side-by-side reference for the most common operations as you migrate.

ConceptPostmarkPostStack
Send transactional emailclient.sendEmail({ From, To, Subject, HtmlBody, TextBody })poststack.emails.send({ from, to, subject, html, text })
Field casingPascalCase: From, To, Subject, HtmlBody, TextBody, MessageStreamlowercase: from, to, subject, html, text, reply_to
Message streamsMessageStream parameter, "outbound" / "broadcast" / customNo per-send stream parameter — send marketing from its own subdomain (e.g. news.yourdomain.com) to keep its reputation separate
Send with a templateclient.sendEmailWithTemplate({ TemplateAlias, TemplateModel })poststack.emails.send({ template_id, variables: { ... } })
Bounce webhook signatureIP allowlist (no signature scheme)X-PostStack-Signature header, HMAC-SHA256
Inbound webhookPOST JSON to your configured URL`email.inbound` webhook event, JSON (different field names)
Suppression managementPOST /suppressions/email/{stream}poststack.suppressions.add({ email, reason })

Migration gotchas

Casing flip (PascalCase → lowercase)

Every Postmark payload uses PascalCase (`From`, `HtmlBody`, `MessageStream`). PostStack uses lowercase field names (`from`, `html`, `reply_to`), and `to` is always an array. TypeScript autocomplete catches missed renames; a quick `sed` is enough for plain JS or Python.

Message streams become sending domains

PostStack has no per-send `MessageStream` parameter. Keep transactional mail on your main domain and send marketing from a dedicated subdomain (for example `news.yourdomain.com`), which keeps the two reputations apart at the mailbox providers. Customers with dedicated IPs can also tag each domain’s IP as transactional or marketing.

Postmark webhooks rely on IP allowlists, not signatures

Postmark does not sign webhook payloads — they expect you to allowlist their source IPs. PostStack signs every webhook with HMAC-SHA256. Update your endpoint to verify the `X-PostStack-Signature` header instead of (or in addition to) the IP check.

Template syntax is close, not identical

Postmark templates use Mustachio (`{{variable}}`, `{{#section}}`). PostStack supports `{{variable}}` merge tags and flat `{{#if}}` / `{{#unless}}` blocks, but not loops or nested sections, and not Postmark’s layouts — inline the layout into each template and render repeated content (such as order line items) in your code.

Migration steps

  1. 1Sign up for PostStack and get your API key
  2. 2Install the PostStack SDK: npm install @poststack.dev/sdk
  3. 3Add your domain and verify DNS records
  4. 4Replace the Postmark SDK with @poststack.dev/sdk
  5. 5Rename Postmark's PascalCase fields to PostStack's lowercase ones
  6. 6Test in development, then switch production traffic

Frequently asked questions

Is PostStack’s deliverability comparable to Postmark?

Postmark is a deliverability gold standard for transactional email, and PostStack follows the same principles — immediate suppression of hard bounces and complaints, RFC 8058 one-click unsubscribe, SPF and DKIM checks before a domain can send, DMARC aggregate report ingestion, TLS-RPT, and blocklist monitoring of our sending IPs. Per-provider bounce and complaint rates (Gmail, Outlook, Yahoo, and others) are visible in your dashboard.

Can I keep my Postmark templates?

Mostly. Export the HTML body from Postmark and save it as a PostStack template. Simple `{{variable}}` substitutions work unchanged; Postmark layouts need to be inlined into each template, and loops or nested sections need to be rendered in your code and passed in as variables.

Does PostStack have message streams?

Not as a per-send parameter. The equivalent on PostStack is a separate sending domain: keep transactional mail on your main domain and send broadcasts from a subdomain such as `news.yourdomain.com`, so mailbox providers judge the two streams separately. With dedicated IPs, each domain’s IP can also be tagged as transactional or marketing.

How long does the migration take?

A focused half-day is typical for a small/medium SaaS — most of the time is in updating field names across the codebase. The DNS propagation step finishes in minutes.

What about my Postmark suppressions?

Export them from Postmark and import the CSV in the PostStack dashboard (up to 10,000 addresses at a time), or add them one by one over the API. The list is enforced from the next send.

Can I run both Postmark and PostStack in parallel during the cutover?

Yes — DNS, SPF, and DKIM accommodate multiple providers simultaneously. Route 10% of traffic to PostStack, verify deliverability matches, then ramp.

More on Postmark vs PostStack

Ready to switch?

Start free. Migrate in under an hour. Save up to 71% on your bill.