Postmark vs AWS SES
Postmark and Amazon SES sit at opposite ends of the managed-vs-infrastructure spectrum. Postmark is a premium managed service: you pay per message for excellent deliverability out of the box, human support, and a dashboard with full message history — a strong default for teams that want transactional email to be someone else's operational problem. SES is a raw sending engine priced near cost; deliverability is largely yours to manage through dedicated IPs, configuration sets, and bounce processing, which rewards teams with the volume to justify the effort. In the unified message model Postmark's structured metadata has no SES equivalent, while neither offers native scheduled sending. The SDK's fail-fast checks flag metadata before a Postmark-to-SES fallback silently drops it, so the cost gap can be exploited without losing data integrity.
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 | AWS SES |
|---|---|---|
| CC recipients | ✓ | ✓ |
| BCC recipients | ✓ | ✓ |
| Reply-To address | ✓ | ✓ |
| Custom headers | ✓ | ✓ |
| Attachments | ✓ | ✓ |
| Tags | ✓ | ✓ |
| Metadata | ✓ | — |
| Scheduled sending (sendAt) | — | — |
Fallback compatibility
Failing over from Postmark to AWS SES loses: metadata. Email SDK rejects the fallback send for messages that use these fields instead of dropping them silently.
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 { ses } from "@opencoredev/email-sdk/ses";
const client = createEmailClient({
adapters: [
postmark({ serverToken: process.env.POSTMARK_SERVER_TOKEN! }),
ses({
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: "us-east-1",
}),
],
defaultAdapter: "postmark",
fallback: ["ses"],
});FAQ
- Can I switch from Postmark to AWS SES 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 AWS SES is a one-line adapter change plus an API key. The SDK fails fast if a message uses a field AWS SES does not support, so nothing is silently dropped.
- Which message fields does Postmark support that AWS SES doesn't?
- Postmark supports metadata in the unified message shape, which AWS SES does not.
- Can I use AWS SES as a fallback for Postmark?
- Partially. Email SDK checks field support before every send: a fallback from Postmark to AWS SES is rejected for messages using metadata. Messages that avoid those fields fail over cleanly.
Adapter docs
Setup guides: Postmark adapter and AWS SES adapter. Sending from your own domain? Verify SPF, DKIM, and DMARC with the free email DNS checker.