Postmark vs SendGrid
Postmark and SendGrid are the classic head-to-head of transactional email. Postmark's pitch is focus: transactional mail only (broadcasts live on a separate stream), consistently fast inbox delivery, 45 days of full-content message history, and pricing that is simple if never the cheapest. SendGrid's pitch is breadth: one platform for transactional and marketing email, dedicated IP options, subusers for multi-tenant setups, and an API that has accumulated every feature over fifteen years — including native scheduled sending, which Postmark lacks. Both support CC, BCC, reply-to, custom headers, attachments, tags, and per-message metadata in the unified message shape, so switching between them with the SDK is nearly lossless; only scheduled sends need a migration plan when moving from SendGrid to Postmark, and the SDK's field checks will flag exactly that.
Message field support
Field support as encoded in Email SDK's own adapter capability matrix — the same data the SDK uses to reject sends that would silently drop fields.
| Message field | Postmark | SendGrid |
|---|---|---|
| CC recipients | ✓ | ✓ |
| BCC recipients | ✓ | ✓ |
| Reply-To address | ✓ | ✓ |
| Custom headers | ✓ | ✓ |
| Attachments | ✓ | ✓ |
| Tags | ✓ | ✓ |
| Metadata | ✓ | ✓ |
| Scheduled sending (sendAt) | — | ✓ |
Fallback compatibility
Failing over from SendGrid to Postmark loses: scheduled sending (sendat).
Same code, either provider
With Email SDK the send call is identical for both providers — only the adapter import changes:
import { createEmailClient } from "@opencoredev/email-sdk";
import { postmark } from "@opencoredev/email-sdk/postmark";
const client = createEmailClient({
adapters: [postmark({ serverToken: process.env.POSTMARK_SERVER_TOKEN! })],
});
await client.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Welcome!",
html: "<p>It works.</p>",
});Or run both, with automatic fallback:
import { createEmailClient } from "@opencoredev/email-sdk";
import { postmark } from "@opencoredev/email-sdk/postmark";
import { sendgrid } from "@opencoredev/email-sdk/sendgrid";
const client = createEmailClient({
adapters: [
postmark({ serverToken: process.env.POSTMARK_SERVER_TOKEN! }),
sendgrid({ apiKey: process.env.SENDGRID_API_KEY! }),
],
defaultAdapter: "postmark",
fallback: ["sendgrid"],
});FAQ
- Can I switch from Postmark to SendGrid without rewriting my email code?
- Yes. With Email SDK both providers share one typed send() call and one message shape, so switching from Postmark to SendGrid is a one-line adapter change plus an API key. The SDK fails fast if a message uses a field SendGrid does not support, so nothing is silently dropped.
- Which message fields does Postmark support that SendGrid doesn't?
- SendGrid supports every message field that Postmark supports, so nothing is lost moving a message from Postmark to SendGrid.
- Can I use SendGrid as a fallback for Postmark?
- Partially. Email SDK checks field support before every send: a fallback from Postmark to SendGrid is rejected for messages using no fields, and from SendGrid to Postmark for messages using scheduled sending (sendat). Messages that avoid those fields fail over cleanly.
Adapter docs
Setup guides: Postmark adapter and SendGrid adapter. Sending from your own domain? Verify SPF, DKIM, and DMARC with the free email DNS checker.