Skip to content
Docs

Guide

Move from Postmark to Emailit. Map servers, message streams and tokens, convert API fields, swap SMTP settings, and move webhooks, suppressions and templates.

Updated Oct 1, 2026

This guide maps Postmark concepts, API calls, webhooks, suppressions and templates to their Emailit equivalents. Read Migrate to Emailit first for the overall order and how to run both providers in parallel.

Concepts

Postmark Emailit
Account Account
Server Workspace, or one workspace with several sending domains
Server API token Sending Only API key, optionally restricted to one domain
Account API token Full Access API key
Transactional message stream The Email API and SMTP relay
Broadcast message stream Campaigns to audiences, or the API with your own List-Unsubscribe header
Inbound message stream Inbound email on a subdomain such as inbound.acme.com
Sender signatures and domains Sending domains. Single-address sender signatures aren’t available.
Templates and layouts Templates with an alias and versions. There are no layouts.
Webhooks per stream Webhooks per workspace
Suppressions per stream One suppression list per workspace
Activity Email APIEmails and Email APILogs
Tag and Metadata meta

To keep transactional and marketing reputation apart, send them from different domains or subdomains, such as mail.acme.com for receipts and news.acme.com for newsletters.

Update your API calls

Postmark’s POST /email with an X-Postmark-Server-Token becomes POST /v2/emails with a bearer token. Field names change from PascalCase to snake_case:

Before: Postmark
curl https://api.postmarkapp.com/email \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -H "X-Postmark-Server-Token: $POSTMARK_SERVER_TOKEN" \
  -d '{
    "From": "Acme <hello@acme.com>",
    "To": "ada@example.com",
    "Subject": "Your receipt",
    "TextBody": "Thanks for your order.",
    "HtmlBody": "<p>Thanks for your order.</p>",
    "MessageStream": "outbound"
  }'
After: Emailit
curl https://api.emailit.com/v2/emails \
  -H "Authorization: Bearer $EMAILIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@acme.com>",
    "to": "ada@example.com",
    "subject": "Your receipt",
    "text": "Thanks for your order.",
    "html": "<p>Thanks for your order.</p>"
  }'
Postmark Emailit
X-Postmark-Server-Token header Authorization: Bearer secret_…
From from
To, Cc, Bcc (comma-separated strings) to, cc, bcc as a string or an array of up to 50 each
ReplyTo reply_to
Subject subject
HtmlBody, TextBody html, text
Headers: [{ "Name": "…", "Value": "…" }] headers: { "Name": "Value" }
Metadata, Tag meta, returned in webhook events
TrackOpens, TrackLinks tracking: { "loads": true, "clicks": true }
Attachments[] with Name, Content, ContentType, ContentID attachments[] with filename, content, content_type, content_id
MessageStream Not needed
POST /email/withTemplate with TemplateAlias or TemplateId and TemplateModel The same POST /v2/emails with template (an alias or ID) and variables
Response with MessageID and ErrorCode: 0 200 with id (em_…), status: "accepted" and ids per recipient. Errors use HTTP status codes.

Emailit has no batch endpoint. Send one request per message, each with up to 50 recipients, and add an Idempotency-Key header so retries are safe. See Send an email.

Switch SMTP settings

Setting Postmark Emailit
Host smtp.postmarkapp.com smtp.emailit.com
Port 587, 2525 or 25 587 (STARTTLS), 465 (TLS), 2525, 2587 or 25
Username Your server API token emailit
Password Your server API token Your Emailit API key

Emailit doesn’t read X-PM-* headers such as X-PM-Message-Stream or X-PM-Tag. Remove them, and set tracking on the domain instead. See SMTP settings.

Map webhook events

Postmark webhook Emailit event
Delivery email.delivered
Bounce, hard bounce types email.bounced
Bounce, soft or transient types email.attempted while Emailit retries, then email.bounced if every retry fails
Spam complaint email.complained
Open email.loaded
Click email.clicked
Subscription change email.unsubscribed for campaign email, and suppression.created or suppression.deleted for suppressions you add or remove with the API
Inbound email.received, then fetch the content with GET /emails/{id}

Emailit also sends email.accepted when the API accepts an email, which Postmark has no webhook for.

The request format changes:

  • Postmark posts one record per request and names it in RecordType. Emailit posts a JSON array of up to 100 events, with the name in type and the email in data.object.
  • Use data.object.id, the em_ ID from the send response, instead of MessageID. Your meta values are in data.object.meta.
  • Postmark webhooks are usually protected with basic authentication credentials in the URL. Emailit signs every request instead: verify X-Emailit-Signature against X-Emailit-Timestamp and your whsec_ secret. See Request signature.
JavaScript
for (const event of req.body) {
  const email = event.data.object;
  if (event.type === 'email.bounced') markBounced(email.to, email.id);
  if (event.type === 'email.complained') unsubscribe(email.to);
}

Move suppressions

  1. Export the suppressions of each Postmark message stream you send from, from the stream’s suppressions page or with the suppressions dump API. Include hard bounces, spam complaints and manual suppressions.

  2. Build one CSV with the columns email,type,reason:

    CSV
    email,type,reason
    old-address@example.com,recipient,postmark hard bounce
    angry@example.com,recipient,postmark spam complaint

    Use the type recipient for addresses that must never receive email. It blocks API, SMTP and campaign sends. For people who only opted out of your broadcasts, use the type unsubscribe, which stops campaigns but still lets transactional email through.

  3. In Email APISuppressions, select Import and upload the file. Each file can have up to 10,000 rows and can be at most 8 MB. Duplicates are skipped.

See Manage suppressions.

Move templates

Copy each template’s HTML from Postmark, including its layout, then import it in Email MarketingTemplates or create it with the Templates API. Emailit has no layouts, so merge the layout and the content into one template. Use the same alias you used in Postmark so your code changes stay small.

Postmark templates use Mustachio. Temple covers simple values and conditions:

Postmark (Mustachio) Emailit (Temple)
{{name}} {{name}}
{{company.name}} {{company.name}}
{{#company}}…{{/company}} sections {{#if company}}…{{/if}}, with full paths such as {{company.name}} inside
{{^name}}…{{/name}} inverted sections {{#if name}}{{else}}…{{/if}}
{{#each items}}…{{/each}} Not supported. Render the list in your code and pass it as one variable.
TemplateModel variables

Temple never escapes HTML, so escape user input before you pass it. A missing value renders as an empty string unless you add a default, such as {{name|"there"}}. See Temple.

Change DNS

Add your domain in Email APIDomains and publish the Emailit records. They use their own names (emailit._domainkey, emailit.<domain>, and optionally go and inbound), so they don’t conflict with Postmark’s DKIM record or its pm-bounces return-path CNAME. Keep your DMARC record. After the cutover, remove the Postmark records. See DNS records.

If you process inbound mail with Postmark, move it to an Emailit inbound subdomain and update the addresses your app gives out. See Set up inbound.

Next steps

Was this page helpful?

Thanks for the feedback.

Thanks, we read every message.