Migrate

Migrate from Postmark

Everything that maps from Postmark to Sendara: the concept map, a before/after send, your suppression list, webhook events, and a parallel-run cutover you can do without a deliverability scare.

Sendara mirrors the surface you already use in Postmark: an API key, a verified sending domain, a send call shaped like { from, to, subject, html }, an event timeline, and signed webhooks. The migration is mostly mechanical. This page maps each piece. For the why and the overall strategy, start with Switch to Sendara.

Keep your Postmark account live and verified throughout. You can verify the same sending domain in both at once and run them in parallel, so nothing here is a one-way door until you decide to decommission.

Why switch

You send from your own domain with your own DKIM, SPF, and DMARC, so your reputation moves with you. Switching providers neither resets nor repairs it. What changes is the developer experience around the send: idempotent requests, a full per-message event timeline, an open suppression model you import and control, and pricing that does not penalize growth. It is a clean API and an honest billing model. It is not a deliverability cure.

Concept map

Here is how the Postmarkprimitives you rely on line up with Sendara's.

PostmarkSendaraNotes
Server API tokenAPI key (sk_live_… / sk_test_…)Create a key in the dashboard. A test key simulates the delivery, and Sendara charges nothing for it.
Sender Signature / DomainDomainPOST /v1/domains returns the 6 records to publish: 3 DKIM CNAMEs, an MX record and an SPF record on the MAIL FROM subdomain, and a DMARC record.
FromfromEach PascalCase Postmark field becomes a lowercase Sendara field. The SDK sends it as metadata.from_email.
client.sendEmailsendara.emails.sendTurn { From, To, Subject, HtmlBody, TextBody } into { from, to, subject, html, text }.
Webhook RecordTypeWebhook eventsSubscribe at POST /v1/webhooks. Sendara signs each delivery with HMAC-SHA256.
SuppressionsSuppression listImport the addresses with POST /v1/suppressions/import.
Message Streams / TemplatesTemplates / BroadcastsCall sendara.templates.* and sendara.broadcasts.*.

Swap the env vars & SDK

Install the Sendara SDK and point your code at a Sendara API key. Create one in the dashboard. Test keys (sk_test_…) simulate delivery and never bill, so you can wire everything up before sending a single real message.

npm install sendara
PostmarkSendara
POSTMARK_SERVER_TOKENSENDARA_API_KEY

Before & after: sending

The send call keeps the same shape. Set the sender to an address on your verified domain with the SDK from parameter. On the wire, that value travels as metadata.from_email. Each SDK call generates an idempotency key. Automatic in-call retries reuse it; a retry started by your application must pass and reuse the same explicit key.

before.ts
import { ServerClient } from "postmark";

const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!);

await client.sendEmail({
  From: "[email protected]",
  To: "[email protected]",
  Subject: "Welcome to Acme",
  HtmlBody: "<strong>It works!</strong>",
});
To show a friendly name, pass a display-name address: from: "Acme <[email protected]>" (name first, address in angle brackets). See sender name.

DNS & domain verification

Verify your domain in Sendara with POST /v1/domains. Publish the 6 records that Sendara returns: 3 DKIM CNAMEs, an MX record and an SPF record on the MAIL FROM subdomain, and a DMARC record. Keep the sender signature of Postmark live, so that you can run both providers together. Move your traffic to Sendara when you are ready.

curl https://api.sendara.dev/v1/domains \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "mail.acme.com" }'
Verify in Sendara without removing anything from Postmark. DKIM supports multiple selectors and SPF can include multiple senders, so both providers can be authorized for the same domain at the same time. See Domains & deliverability.

Import your suppression list

Export your active Postmark suppressions. Import bounces and spam complaints with state suppressed; import marketing opt-outs with state unsubscribed. Do not import reactivated addresses. Sendara then suppresses every new hard bounce and complaint automatically.

Import in bulk with POST /v1/suppressions/import. It is an admin-scoped, idempotent endpoint that upserts each entry and returns { imported, skipped }, so re-running the import is safe. Use state: "suppressed" (the default) for hard bounces, complaints, and manual hard blocks; use state: "unsubscribed" for marketing opt-outs so transactional mail remains allowed. The import does not rewrite a matching contact record. After cutover, hard bounces and spam complaints are added to the list automatically. You can also manage individual entries with POST/DELETE /v1/suppressions.

curl https://api.sendara.dev/v1/suppressions/import \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "entries": [
      { "email": "[email protected]", "reason": "hard_bounce", "state": "suppressed" },
      { "email": "[email protected]", "reason": "provider_unsubscribe", "state": "unsubscribed" }
    ]
  }'
Import suppressions before your first production send on Sendara. A send to a suppressed address is recorded as suppressed and never enqueued or billed. That is the safety net that stops a fresh provider from re-mailing the addresses your old one already learned to avoid.

Map your webhook events

Postmark posts one event for each request, and marks it with a `RecordType`. Map that value to the matching Sendara event type. Sendara signs an HMAC-SHA256 of `${timestamp}.${rawBody}` in hex. Check the signature with verifyWebhook().

Postmark eventSendara event
Deliverydelivered
Bouncebounced
SpamComplaintcomplained
Openopened
Clickclicked
SubscriptionChangesuppression stateSendara fires no webhook event. Import active Postmark opt-outs with state unsubscribed, hard suppressions with state suppressed, and do not import reactivated addresses.

Subscribe with POST /v1/webhooks, then verify each delivery with verifyWebhook() from the SDK. See Webhooks and Events & the message timeline for the full payload schemas and delivery guarantees.

Cutover checklist

Run this in order. You can stop at any step and stay there as long as you like. Nothing is irreversible until you decommission Postmark.

  1. Verify the domain in Sendara and publish the DKIM, SPF, DMARC, and MAIL FROM records, leaving Postmark verified.
  2. Import your suppression list via POST /v1/suppressions/import so no known-bad address is ever mailed.
  3. Wire up the SDK and webhooks against a test key, remapping the events from the table above.
  4. Mirror a low-stakes stream through Sendara and watch it in the message log. Confirm each send with GET /v1/messages?idempotency_key=… and compare delivered, bounced, and complained rates against Postmark.
  5. Ramp traffic up as the numbers hold. There are no IPs to warm when you stay on the same domain, so move as fast as your confidence allows.
  6. Cut over the remainder, keep Postmark verified for a week as a fallback, then decommission it.
The idempotency_key you already pass on each send is also your status handle. Behind a feature flag, you can replay a request against Sendara without double-sending, then look it up with GET /v1/messages?idempotency_key=… to confirm it delivered. See Message lifecycle.