New from emailrHealthCheck Email iconHealthCheck Email
emailr_

Migrate from Postmark to emailr_

Move from Postmark to emailr_: map servers, message streams, templates, tags and metadata, translate webhooks and import suppressions step by step.

Postmark iconPostmarkemailr_

Postmark is built around servers and message streams. emailr_ is built around domain workspaces, with transactional sends going through the API and marketing sends through Broadcasts. This guide shows how each piece maps across.

The code change is mostly renaming fields: Postmark uses PascalCase (HtmlBody, TextBody, MessageStream), emailr_ uses lowercase fields in the same spirit.

The code change

A typical Postmark send call next to its emailr_ equivalent.

Before (Postmark)ts
import { ServerClient } from 'postmark'

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

await client.sendEmail({
  From: '[email protected]',
  To: '[email protected]',
  Subject: 'Your receipt',
  HtmlBody: '<p>Thanks for your order.</p>',
  Tag: 'receipts',
  Metadata: { order_id: '123' },
  MessageStream: 'outbound',
})
After (emailr_)ts
import { Emailr } from 'emailr'

const emailr = new Emailr({ apiKey: process.env.EMAILR_API_KEY! })

await emailr.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Your receipt',
  html: '<p>Thanks for your order.</p>',
  tags: { category: 'receipts', order_id: '123' },
})

Postmark concepts in emailr_

Use this table to find where each feature you rely on lives in emailr_.

Postmarkemailr_Notes
Server API tokenAPI key (et_live_ prefix)
ServersDomain workspacesEach key is scoped to a workspace.
Transactional message streamemails.send() API
Broadcast message streamBroadcasts
Templates (Mustachio, TemplateModel)Templates (template_id + template_data)
Tag + Metadatatags object
Suppressions per streamContacts with subscribed: false, plus Topics
Inbound streamInboxes and email.received webhooks

How to migrate from Postmark

  1. 01

    Create an API key

    Create a key under API keys in the dashboard. Keys start with et_live_ and are scoped to one domain workspace. Store it as EMAILR_API_KEY in your secret manager or environment, never in client-side code.

  2. 02

    Add and verify your sending domain

    In the dashboard, open Domains and add the domain you send from. A subdomain such as mail.yourcompany.com keeps transactional reputation separate from your corporate mailbox.

    Emailr generates the DNS records: a DKIM TXT record on the emailr._domainkey selector, an SPF TXT record, and an MX + TXT pair on the send subdomain used as the MAIL FROM / Return-Path. A DMARC record is optional but recommended.

    Postmark's DKIM record uses a dated …pm._domainkey selector and a pm-bounces Return-Path CNAME. Neither conflicts with Emailr's records.

    Emailr's DKIM selector and send subdomain don't collide with other providers' records, so you can add them before cutting over and send from both providers during the migration. A domain can only have one SPF record: if one already exists, merge Emailr's include: into it instead of adding a second record.

  3. 03

    Swap the SDK

    Install the SDK and rename fields: From → from, To → to, HtmlBody → html, TextBody → text, ReplyTo → reply_to_email. Drop MessageStream: API sends are transactional by default. Tag and Metadata merge into tags.

    Postmark attachments (Name, Content, ContentType) become filename, content and contentType.

    Terminalbash
    npm install emailr
  4. 04

    Move templates

    Replace sendEmailWithTemplate({ TemplateAlias, TemplateModel }) with emails.send({ template_id, template_data }). Template-level subject, from and reply-to are used when you don't override them.

    Emailr templates substitute flat {{variable}} tokens from template_data. There are no loops or conditionals, so pre-render lists and optional blocks in your code and pass the result as a variable.

  5. 05

    Move broadcast streams and suppressions

    Newsletters sent on a Postmark broadcast stream move to emailr_ Broadcasts. Import each stream's suppressions as contacts with subscribed: false, or use Topics if you need per-category opt-outs.

  6. 06

    Recreate your event webhooks

    Postmark configures webhooks per server and stream. In emailr_ create one transactional webhook per workspace and branch on the event field.

    Create a webhook of type transactional (delivery and engagement events), domain (verification status) or receiving (inbound email). Each webhook has its own signing secret. Every request carries an X-Emailr-Signature header: a hex-encoded HMAC-SHA256 of the raw request body. Verify it before trusting the payload.

    verify-webhook.tsts
    import { createHmac, timingSafeEqual } from 'node:crypto'
    
    // rawBody must be the exact request body string, before JSON.parse
    export function isValidEmailrWebhook(rawBody: string, signature: string | null) {
      if (!signature) return false
      const expected = createHmac('sha256', process.env.EMAILR_WEBHOOK_SECRET!)
        .update(rawBody)
        .digest('hex')
      const a = Buffer.from(expected)
      const b = Buffer.from(signature)
      return a.length === b.length && timingSafeEqual(a, b)
    }
    
    // const ok = isValidEmailrWebhook(body, req.headers.get('x-emailr-signature'))
    // const { event, data } = JSON.parse(body)  // e.g. event === 'email.bounced'

Webhook event mapping

emailr_ webhooks post one JSON event per request with an event name and a data object. Translate event names in your handler with this table.

Postmark eventemailr_ event
Deliveryemail.delivered
Bounce (hard)email.bounced
SpamComplaintemail.complained
Openemail.opened
Clickemail.clicked

Things to watch for

  • Postmark accepts a single Tag string; emailr_ tags are key/value pairs, so pick a key such as category.
  • Postmark's batch endpoint sends up to 500 messages per call. With emailr_, send individually or use Broadcasts for audience sends.
  • Mustachio sections ({{#items}}) aren't supported in emailr_ templates; render repeated blocks in code.

Frequently asked questions

What replaces Postmark servers in emailr_?

Domain workspaces. A workspace groups a primary domain, its aliases, templates, contacts and inboxes, and new API keys are scoped to a single workspace.

How do Postmark message streams map to emailr_?

Transactional streams map to the emails API. Broadcast streams map to emailr_ Broadcasts, which handle unsubscribe links and skip unsubscribed contacts automatically.

Can I move my Postmark templates?

Yes, recreate them as emailr_ templates. Variables written as {{name}} carry over directly; Mustachio sections and partials need to be pre-rendered in code.

Does emailr_ support inbound email like Postmark's inbound stream?

Yes. Create an inbox for the address. Incoming messages are threaded and you can receive email.received webhooks for each one.

Ready to leave Postmark?

Create a free account, verify your domain and send your first email through emailr_ in minutes. No credit card required.