Switch from Postmark
Replace Postmark's transactional Email API with Tinbox while keeping the postmark ServerClient for sendEmail and sendEmailBatch.
Tinbox can replace the API side of a Postmark transactional integration when
your app mainly uses postmark.ServerClient#sendEmail or sendEmailBatch.
Postmark's Node SDK accepts a custom requestHost, but not a path prefix, so
Tinbox exposes root aliases for the official SDK: /email and /email/batch.
That means the trial is still tiny: keep postmark, change the token, and point
one host option at Tinbox.
What changes
| Postmark project setting | Tinbox replacement |
|---|---|
| Postmark server token | TINBOX_API_KEY=tbx_... |
api.postmarkapp.com | <your-tinbox-host> |
X-Postmark-Server-Token | The official SDK keeps this header, with the Tinbox key as its value |
| Postmark activity feed | Tinbox send ledger and delivery events |
| Postmark sender signature/domain | Tinbox sending domain verification |
One-line test drive
Keep your existing client.sendEmail(...) and client.sendEmailBatch(...)
calls. Change the ServerClient host option:
const client = new postmark.ServerClient(process.env.TINBOX_API_KEY!, {
useHttps: true,
requestHost: "mail.yourdomain.com",
});
Tinbox handles the SDK's root /email and /email/batch requests through
Postmark-compatible aliases.
Inventory your Postmark usage
rg "from ['\"]postmark['\"]|new postmark\\.ServerClient|sendEmail\\(|sendEmailBatch\\("
rg "sendEmailWithTemplate|MessageStream|TrackOpens|TrackLinks|Metadata|Tag"
| Usage | Migration path |
|---|---|
sendEmail with rendered HtmlBody or TextBody | Keep the Postmark SDK |
sendEmailBatch | Keep the Postmark SDK; Tinbox maps each message |
From, To, Cc, Bcc, ReplyTo, Subject | Supported |
Headers and base64 Attachments | Supported with limits |
| Template APIs | Render before calling Tinbox |
MessageStream, Tag, Metadata, tracking flags | Accepted as warnings or metadata, not analytics parity |
Configure the SDK
TINBOX_HOST=mail.yourdomain.com
TINBOX_API_KEY=tbx_...
import postmark from "postmark";
const client = new postmark.ServerClient(process.env.TINBOX_API_KEY!, {
useHttps: true,
requestHost: process.env.TINBOX_HOST!,
});
await client.sendEmail({
From: "hello@yourdomain.com",
To: "ada@example.com",
Cc: "billing@example.com",
Bcc: "audit@example.com",
ReplyTo: "support@yourdomain.com",
Subject: "Your receipt",
HtmlBody: "<p>Thanks.</p>",
TextBody: "Thanks.",
Headers: [{ Name: "X-Receipt-ID", Value: "receipt-4821" }],
Attachments: [
{
Name: "receipt.txt",
Content: Buffer.from("receipt").toString("base64"),
ContentType: "text/plain",
ContentID: "",
},
],
Metadata: { tinbox_idempotency_key: "receipt-4821" },
});
The official SDK posts to:
POST /email
POST /email/batch
Tinbox also keeps canonical provider routes for direct callers:
POST /api/send/compat/postmark/v1/email
POST /api/send/compat/postmark/v1/email/batch
Batch sends
await client.sendEmailBatch([
{
From: "hello@yourdomain.com",
To: "ada@example.com",
Subject: "Your receipt",
TextBody: "Thanks.",
},
{
From: "hello@yourdomain.com",
To: "grace@example.com",
Subject: "Your receipt",
TextBody: "Thanks.",
},
]);
Field mapping
| Postmark field | Tinbox support | Notes |
|---|---|---|
From, To, Cc, Bcc | Yes | Comma-separated recipient strings are parsed. |
ReplyTo | Yes | Maps to Tinbox reply-to. |
Subject | Yes | Per message. |
HtmlBody, TextBody | Yes | At least one body part is required. |
Headers | Partial | Custom X-* headers only. |
Attachments | Yes | Base64 Content is required. |
Metadata | Partial | Useful for correlation; not Postmark analytics parity. |
MessageStream, Tag, TrackOpens, TrackLinks | Warning | Accepted but degraded to Tinbox logs/events. |
| Template APIs | No | Render before send. |
| Inline CID attachments | No | Not drop-in yet. |
Warnings and failures
Template sends fail with 422 when Tinbox cannot safely honor them. Degraded
analytics fields return X-Tinbox-Compat-Warnings while keeping a Postmark-like
response shape.
Tinbox-native path
import { Tinbox } from "tinbox/sdk";
const tinbox = new Tinbox({
host: `https://${process.env.TINBOX_HOST}`,
apiKey: process.env.TINBOX_API_KEY!,
});
await tinbox.emails.send({
from: "hello@yourdomain.com",
to: "ada@example.com",
subject: "Your receipt",
html: "<p>Thanks.</p>",
idempotencyKey: "receipt-4821",
});
Cutover checklist
- Verify the Tinbox sending domain.
- Create a Tinbox key scoped to the Postmark
Fromaddress. - Configure
ServerClientwithrequestHost. - Smoke test both
sendEmailandsendEmailBatch. - Replace Postmark templates, streams, tags, and tracking dashboards with app-side rendering and Tinbox delivery visibility.
Source note
This guide is validated against postmark v5.0.0. The SDK mirror test records
the exact method, root alias path, X-Postmark-Server-Token auth style, content
type, and JSON body emitted by the official package.