Postmark's pitch is one thing: deliverability. They actively police their senders, refuse marketing email, and run separate IPs for transactional traffic. Mail from Postmark lands in the inbox more reliably than from any general-purpose provider — at a slightly higher per-email price.
Sign up at postmarkapp.com ↗. Free tier: 100 emails/month forever. Trial: 100 emails/month, then $15/mo for 10K.
Create a Server — Postmark's term for a project. Each server has its own API token. Common pattern: one server per environment (production, staging).
Postmark won't let you send from @yourdomain.com until you verify ownership. Server → Sender Signatures → Add Domain.
Add three DNS records at your DNS provider:
Postmark verifies automatically; usually green within 30 minutes.
Optionally add DMARC too. Postmark has a great free DMARC analyzer ↗ that emails you weekly reports.
Server → API Tokens. Copy the Server API token:
POSTMARK_SERVER_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Different from the Account API token (which is for managing servers; not for sending). The server token is what your app uses to send mail.
npm install postmark
Other languages: Python, Ruby, PHP, Java, Go, .NET ↗. Or just use plain HTTP — the API is a small JSON surface.
import { ServerClient } from "postmark";
const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN);
await client.sendEmail({
From: "[email protected]",
To: "[email protected]",
Subject: "Hello from Postmark",
TextBody: "Plain text version",
HtmlBody: "<p>HTML body</p>",
MessageStream: "outbound", // "outbound" = transactional; default
});
Check your recipient inbox. If it arrives, you're done. If it doesn't, the Postmark dashboard → Activity shows every send attempt with its status and the receiving server's response.
For emails you send repeatedly (welcome, password reset, receipts), use Postmark Templates — they're editable in the dashboard without redeploying your app.
Server → Templates → Create template. Postmark uses Mustache ↗ syntax for variables:
<p>Hi {{name}},</p>
<p>Click here to verify: {{verifyUrl}}</p>
Send by ID or alias:
await client.sendEmailWithTemplate({
From: "[email protected]",
To: user.email,
TemplateAlias: "welcome", // or TemplateId: 12345
TemplateModel: {
name: user.name,
verifyUrl: `https://yourapp.com/verify/${token}`,
},
});
For richer template DX, the open-source MJML ↗ language compiles to email-safe HTML and works with Postmark templates. Postmark also has free pre-designed templates ↗ you can import.
Postmark separates transactional (one-to-one, in response to a user action) from broadcast (one-to-many, like newsletters). They use different IP pools so a bad broadcast doesn't poison your transactional reputation.
If you only do transactional, you don't need to touch this. If you also do broadcast, create a second stream and pass MessageStream: "broadcast" for those sends.
Server → Settings → Tracking. Toggle:
For password-reset / magic-link mail, turn click tracking off — it rewrites the URL and some email clients pre-fetch URLs, accidentally consuming single-use tokens.
Server → Webhooks → Add webhook. Pick events: bounces, spam complaints, opens, clicks, delivery. Postmark POSTs your URL with event details.
Most important: handle hard bounces by suppressing the recipient. Sending to invalid addresses is the fastest way to ruin your sending reputation. The Postmark client lets you query suppressions, and the bounce webhook gives you a structured event to act on.
Free: 100 emails/month forever.
Paid: starts $15/mo for 10K emails. Scales linearly past that. Postmark pricing ↗.
Per-email cost is roughly 2–3× Resend's and 10× AWS SES's. You're paying for deliverability — Postmark routinely places higher in independent inbox-placement tests.