JustEmails
PricingSign inStart free trialStart free
Tutorials··14 min read

Migrate from Postmark to JustEmails API: 2026 Cutover Guide

Postmark to JustEmails migration without losing emails.

By JustEmails Platform Team
Contents
  1. What You'll Have at the End
  2. Prerequisites
  3. Step 1: Audit Your Postmark Setup
  4. Step 2: Verify Your Domain in JustEmails
  5. Step 3: Convert Templates to Local Rendering
  6. Step 4: Build the Dual-Send Wrapper
  7. Step 5: Update Webhook Handlers
  8. Step 6: The 72-Hour Watch
  9. Step 7: Clean Cutover
  10. Common Errors and Fixes
  11. What This Saves You
  12. Next Steps
  13. Frequently Asked Questions
  14. Why would I migrate from Postmark to JustEmails?
  15. Will I lose emails during the Postmark to JustEmails migration?
  16. How do I migrate Postmark templates to JustEmails?
  17. Do I need to re-verify my domain when migrating from Postmark?
  18. Try JustEmails

Picture the month-end invoices: transactional email on Postmark, mailbox hosting somewhere else, a separate Resend account for the side project. Three providers, three dashboards, three sets of credentials. It's a mess.

Look, Postmark is genuinely good. Excellent deliverability. Clean dashboard. Their bounce handling? Widely respected. This isn't a hit piece.

But when you're already using JustEmails for mailbox hosting across multiple domains, and you realize the $49/year plan includes 1,000 transactional emails/month with a straightforward API, the consolidation math starts to look attractive. One provider for mailboxes and transactional. One DNS configuration per domain. One invoice. (Setup takes longer than you'd guess, mostly because of DNS propagation and template conversion. More on that later.)

This is a cutover guide for moving a live service. The goal: zero dropped emails, minimal downtime risk, and a clean break from the old provider.

What You'll Have at the End

A fully working transactional email setup on the JustEmails API — password resets, order confirmations, notifications, whatever you're sending — with your existing domain verified, webhooks receiving delivery events, and Postmark ready to decommission. Hands-on time: 2-3 hours spread across a week. Maybe four if your templates are a tangle.

Prerequisites

Before you start:

  • A JustEmails account with your sending domain already added (if you're using JustEmails for mailboxes, it's already there)
  • Access to your DNS provider (Cloudflare, Route53, Namecheap, wherever)
  • Admin access to your Postmark account to export templates and grab current DNS records
  • Your application codebase with access to where transactional emails are sent
  • Basic familiarity with HTTP APIs and your stack's HTTP client

One thing people skip: check your current Postmark sending volume. Log into Postmark, go to Activity, and note your daily/monthly numbers. You'll need this to pick the right JustEmails API tier. The $49/year plan includes 1,000/month; add $25/year for each additional 10K/month tier. Skip this step and you may find out mid-migration that you need a bigger tier.

Note the daily figure especially, because the daily ceiling binds before the monthly allowance does: a newly verified domain sends no more than 50 a day in the first week, 200 in the second, and up to 500 from day 15, with mailbox sends and API sends counted against the same per-account number. A tier raises the monthly allowance rather than that daily cap, which puts the practical maximum at about 15,000 a month. Warm-up is measured from domain verification rather than signup, so adding this sending domain to a long-standing account starts that domain back at 50 a day.

API access requires a paid plan. This cutover runs on the transactional API, and API access is not included in the 7-day trial — a trial account cannot issue the key Step 2 asks for, so there is no way to complete the migration without subscribing first.

Step 1: Audit Your Postmark Setup

Don't start changing code until you know exactly what you're migrating.

In Postmark, document:

Message Streams: Postmark separates transactional and broadcast streams. List each stream and what it sends. JustEmails doesn't enforce this separation at the API level, but you'll want to categorize for your own sanity.

Templates: Export every template. Postmark's API has a GET /templates endpoint (it requires count and offset), or you can manually copy from the dashboard. The list gives you names and IDs; fetch each template by ID for its HTML, text version and variable names.

curl "https://api.postmarkapp.com/templates?count=100&offset=0" \
  -X GET \
  -H "Accept: application/json" \
  -H "X-Postmark-Server-Token: your-token" > postmark-templates.json

Webhook URLs: Note every webhook endpoint you've configured — deliveries, bounces, spam complaints, opens, clicks. You'll replicate these for JustEmails.

Sending volume: Average daily sends, peak sends, monthly total. This determines your tier.

Twenty minutes here saves you from discovering a forgotten webhook halfway through the cutover.

Step 2: Verify Your Domain in JustEmails

If you're already using JustEmails for mailboxes on this domain, the domain is verified already; what you still need is an API key and a from address.

In JustEmails dashboard:

  1. Generate an API key under Settings → API keys if you don't have one — API keys are paid-plan only, a trial account can't create one
  2. Open your domain's settings and copy the DNS records the setup screen shows for the domain (the DKIM key is unique per domain)
  3. Make sure the address you send from (say noreply@yourdomain.com) exists as a mailbox or alias on the domain — the send endpoint refuses any other from address

If this is a new domain to JustEmails, add it now and complete the verification flow. You'll add a TXT record for domain ownership.

DNS changes — the tricky part:

JustEmails signs from dkim._domainkey, a TXT record whose key is unique to your domain. Postmark uses its own DKIM selector (usually 20xxxxxx._domainkey). Different selectors, so during migration you can publish both.

Add the JustEmails DKIM record alongside Postmark's, copying the value from your dashboard rather than from this page:

dkim._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=…"

Update your SPF record to authorise both (temporarily). Postmark uses an include:; we use an a: mechanism pointed at mail1.justemails.app, so the two sit side by side:

v=spf1 include:spf.mtasv.net a:mail1.justemails.app ~all

Don't remove Postmark's records yet. You're running both in parallel. Removing them before you've fully migrated will break DKIM alignment on any emails still going through Postmark. For a deep dive on email authentication, see our SPF, DKIM, and DMARC setup guide.

Step 3: Convert Templates to Local Rendering

Here's where Postmark and JustEmails differ philosophically.

Postmark stores templates on their servers. You send a template ID and variables; they render it. JustEmails expects you to render templates in your application and send the final HTML.

The migration approach:

  1. Export your Postmark templates (you did this in step 1)
  2. Convert them to your templating engine — Handlebars, Liquid, Jinja2, EJS, whatever you use
  3. Store them in your codebase (or a headless CMS if you prefer)
  4. Render before calling the JustEmails API

Example — before (Postmark):

// Postmark's approach
await postmark.sendEmailWithTemplate({
  TemplateId: 12345,
  TemplateModel: {
    name: 'Sarah',
    resetLink: 'https://app.com/reset?token=abc'
  },
  To: 'sarah@example.com'
});

After (JustEmails with local template):

// Local template rendering
import Handlebars from 'handlebars';
import { passwordResetTemplate } from './templates/password-reset.js';

const template = Handlebars.compile(passwordResetTemplate);
const html = template({
  name: 'Sarah',
  resetLink: 'https://app.com/reset?token=abc'
});

await fetch('https://justemails.app/api/v1/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.JUSTEMAILS_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    from: 'YourApp [noreply@yourdomain.com](mailto:noreply@yourdomain.com)',
    to: ['sarah@example.com'],
    subject: 'Reset your password',
    html: html,
    text: 'Click here to reset: ...' // plain text fallback
  })
});

Is this more work? Yes. Honestly, it can feel like a step backward from Postmark's convenience. But your templates then live in version control, you can preview them locally, and you're not locked into a provider's template syntax. I think that trade is worth it. Your mileage may vary.

Step 4: Build the Dual-Send Wrapper

Here's the core migration technique: send through JustEmails first, fall back to Postmark if it fails. Run this for 72 hours minimum. If JustEmails handles everything, you're good to cut Postmark.

// email-sender.js — dual-send wrapper
async function sendTransactional({ to, subject, html, text, idempotencyKey }) {
  // Try JustEmails first
  try {
    const response = await fetch('https://justemails.app/api/v1/send', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.JUSTEMAILS_API_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey // one key per message, reused on retries
      },
      body: JSON.stringify({
        from: 'YourApp [noreply@yourdomain.com](mailto:noreply@yourdomain.com)',
        to: [to],
        subject,
        html,
        text
      })
    });

    if (response.ok) {
      console.log('Sent via JustEmails');
      return { provider: 'justemails', success: true };
    }

    throw new Error(`JustEmails failed: ${response.status}`);
  } catch (error) {
    console.warn('JustEmails failed, falling back to Postmark:', error.message);
  }

  // Fallback to Postmark
  try {
    const response = await fetch('https://api.postmarkapp.com/email', {
      method: 'POST',
      headers: {
        'X-Postmark-Server-Token': process.env.POSTMARK_TOKEN,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        From: 'noreply@yourdomain.com',
        To: to,
        Subject: subject,
        HtmlBody: html,
        TextBody: text
      })
    });

    if (response.ok) {
      console.log('Sent via Postmark (fallback)');
      return { provider: 'postmark', success: true };
    }

    throw new Error(`Postmark also failed: ${response.status}`);
  } catch (error) {
    console.error('Both providers failed:', error.message);
    throw error;
  }
}

The idempotency key makes a retry safe: if a request times out but actually went through, re-sending it with the same key won't send a second copy. JustEmails supports idempotency keys on the send endpoint — use them. Generate one key per message and reuse it only when retrying that message. Don't derive it from the address and subject: the API answers 409 when a key is reused with a different body, so a user's second password reset would fail over to Postmark.

Step 5: Update Webhook Handlers

Postmark sends webhooks to your endpoints for bounces, spam complaints, deliveries, etc. JustEmails does too, but the payload format differs.

Create a new webhook endpoint or modify your existing one to handle both formats during transition:

// /api/email-webhooks.js
export async function POST(request) {
  const payload = await request.json();

  // Detect provider by payload shape
  if (payload.RecordType) {
    // Postmark format
    return handlePostmarkWebhook(payload);
  } else if (payload.event) {
    // JustEmails format
    return handleJustEmailsWebhook(payload);
  }

  return new Response('Unknown format', { status: 400 });
}

function handleJustEmailsWebhook(payload) {
  // JustEmails posts { event, timestamp, data }
  switch (payload.event) {
    case 'email.delivered':
      // record delivery
      break;
    case 'email.bounced':
      // mark address as undeliverable
      break;
  }
}

Register your webhook URL in the JustEmails dashboard under Settings → Webhooks, and check the API docs for the full list of event names. Start receiving both providers' events simultaneously.

Step 6: The 72-Hour Watch

Deploy the dual-send wrapper to production. Then watch.

Check daily:

  • JustEmails → sends, bounces and complaints (from the webhook events)
  • Your application logs → How many fell back to Postmark?
  • Webhook endpoint logs → Are both providers' events arriving?

If JustEmails handles 100% of sends for 72 hours with no fallbacks, you're ready to cut Postmark.

If you see fallbacks, investigate. Common causes:

  • Rate limits (a 429's error.code names which one; while the domain is warming up the daily ceiling is 50, then 200, and no tier lifts it)
  • Domain verification issues (DKIM/SPF not propagated)
  • API key permissions

Don't rush this step. Three days of parallel sending is cheap insurance.

(Resist turning Postmark off after 24 hours because "everything looks fine." A misconfigured webhook can hide for a day and surface as bounces you never saw.)

Step 7: Clean Cutover

Once you're confident:

  1. Remove Postmark fallback from code. Delete the fallback block from your email sender. JustEmails is now the only path.

  2. Update DNS, 48 hours later. Once mail already queued at Postmark has had time to arrive, remove Postmark's DKIM records and its SPF include, leaving only our a: mechanism. Your SPF becomes:

    v=spf1 a:mail1.justemails.app ~all
    

    (Plus any other legitimate senders — marketing tools, etc.)

  3. Disable Postmark webhooks. Either delete them or point them to a sink endpoint.

  4. Keep Postmark account active for 30 days. In case you missed something. You can cancel after a month of clean operation.

  5. Update your runbooks. If you have incident response docs that mention "check Postmark dashboard," update them.

Common Errors and Fixes

401 Unauthorized from JustEmails API

The API key isn't reaching the header intact. JustEmails expects Authorization: Bearer <your key>. If you copied from an environment variable, check for trailing newlines or spaces.

It's always whitespace. Always.

429 Too Many Requests

Either the monthly allowance (base tier is 1,000/month; add tiers at $25/year per 10K/month), the daily send cap, which a tier does not lift, or an hourly limit. The response's error.code names which one. Wait for the Retry-After header rather than hammering the endpoint.

DKIM failures after cutover

You removed Postmark's DKIM records before all Postmark-sent emails were delivered. DKIM is checked on receipt, not send. If an email was queued at Postmark and delivered after you removed records, the signature fails. This is why you keep the records for 48 hours after cutover.

Templates rendering with wrong variables

Postmark's Mustachio syntax isn't 100% compatible with Handlebars/Liquid. Watch for edge cases like {{#if}} vs {{#unless}} and array iteration syntax. Test every template manually. Yes, all of them. It's tedious.

What This Saves You

For a service sending 10,000 transactional emails per month:

  • Postmark: check their pricing page for your volume
  • JustEmails (if you're already using it for mailboxes): the $49/year base includes 1K/month, and one 10K tier adds $25/year, covering mailboxes and transactional together

The real savings isn't dollars — it's one fewer vendor to manage. One login, one DNS configuration, one support contact. Once everything sits on one provider, there's no more guessing which one handles what.

Fewer invoices. Fewer dashboards. Fewer passwords to rotate. It adds up.

Is Postmark's deliverability slightly better? Historically, yes — they've built reputation over a decade. But for most transactional email (password resets, receipts, notifications), the difference is negligible. You're not sending cold outreach through this channel.

For the rest of us consolidating SaaS tools, the JustEmails API handles the job.

Next Steps

Now that you've migrated:

  • Monitor deliverability with Google Postmaster Tools for the first month. Watch for reputation changes on your domain.
  • Set up DMARC reporting if you haven't already. We covered the p=none to p=reject progression in our DMARC enforcement guide.
  • Consider consolidating marketing email too, but only a small one — a low-volume opt-in newsletter can go out through JustEmails if it fits inside the same daily ceiling (500 a day at most) and the 100-recipients-an-hour limit. High-volume marketing belongs in a dedicated marketing tool.

The JustEmails API docs cover attachments, open and click tracking, and the full webhook event list. Contact support@justemails.app if you hit edge cases during migration.

And if you're not on JustEmails yet but considering it alongside Postmark — see our SMTP vs API comparison for when HTTP API makes sense versus relay. Spoiler: if you're already on Postmark's API, you're the target audience.

Frequently Asked Questions

Why would I migrate from Postmark to JustEmails?

Not because Postmark is bad — Postmark's deliverability is excellent. You'd migrate to consolidate mailbox hosting and transactional email under one provider. If you're already paying $49/year for JustEmails mailboxes, you get 1,000 transactional emails/month included. Adding tiers is $25/year per 10K emails/month. For teams inside the 500-a-day send ceiling — roughly 15,000 a month, with mailbox and API sends counted together — consolidation often makes sense.

Will I lose emails during the Postmark to JustEmails migration?

Not if you use the dual-send window approach. Keep both providers active for 72 hours minimum. Send through JustEmails first (so it builds reputation on your domain), then fall back to Postmark if JustEmails fails. Once you've confirmed zero failures over 72 hours, remove the Postmark fallback.

How do I migrate Postmark templates to JustEmails?

JustEmails doesn't have server-side template storage like Postmark. You'll render templates in your application code — Handlebars, Liquid, Jinja, whatever your stack uses. Export your Postmark templates via their API, convert them to your local template engine, and pass the rendered HTML to the JustEmails send endpoint. It's more work upfront but gives you version control over templates.

Do I need to re-verify my domain when migrating from Postmark?

Only if the domain isn't on JustEmails yet. Either way, each provider has its own DKIM keys. You'll add JustEmails' DKIM records alongside Postmark's during the transition, then remove Postmark's after cutover. SPF needs updating too — add our a:mail1.justemails.app mechanism (we don't publish an SPF include), keep Postmark's include until you're fully migrated, then remove it. DMARC stays the same; just watch alignment reports.


Try JustEmails

JustEmails is $49 a year, flat, for unlimited custom domains and unlimited mailboxes with a unified inbox across all of them — additional team members are $2 per member per month, and storage beyond the included 10 GB is $99 a year per 100 GB. Built for agencies, freelancers, and anyone managing email across more than one domain.

Start your 7-day free trial → · How it compares

postmark-migrationtransactional-email-apiemail-api-migrationwebhook-handlersdkim-spf-setupbuildinpublicsaasstudioaiworkforcebuildwithclaude

Related posts

Tutorials
Proxmox SMTP Relay: Alert Email from PVE and Backup Server
13 min read
Tutorials
Synology SMTP Alerts and TrueNAS Email Setup That Actually Sends
12 min read
Comparisons
Kolab Now vs JustEmails: Swiss Groupware or Flat-Fee Domain Email?
12 min read