Envello
Postmark alternative

Switching from Postmark? Move your transactional email to Envello

A step-by-step guide: DNS setup alongside your existing records, the sending-code swap, a dual-send cutover window, and verification with 90 days of searchable logs on Pro.

Postmark's API uses PascalCase JSON fields and a server token header; Envello uses lowercase fields and a Bearer token. The send call is a mechanical rename. Server-side templates with logic and Message Streams are the two parts that need a decision.

Before: Postmark Node SDK
import { ServerClient } from "postmark";

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

await client.sendEmail({
  From: "Acme <[email protected]>",
  To: "[email protected]",
  Subject: "Welcome",
  HtmlBody: renderedHtml,
  MessageStream: "outbound",
});
After: Envello Node SDK
import { Envello } from "envello";

const envello = new Envello(process.env.ENVELLO_API_KEY);

await envello.emails.send({
  from: "Acme <[email protected]>",
  to: "[email protected]",
  subject: "Welcome",
  html: renderedHtml,
});
Before: Postmark REST
curl -X POST 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 <[email protected]>",
    "To": "[email protected]",
    "Subject": "Welcome",
    "HtmlBody": "<p>Welcome aboard</p>"
  }'
After: Envello REST
curl -X POST https://api.envello.dev/emails \
  -H "Authorization: Bearer $ENVELLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <[email protected]>",
    "to": "[email protected]",
    "subject": "Welcome",
    "html": "<p>Welcome aboard</p>"
  }'
Field mapping

Postmark fields and their Envello equivalents

PostmarkEnvello
X-Postmark-Server-Token headerAuthorization: Bearer <API key>
From, To, Subjectfrom, to, subject
HtmlBody, TextBodyhtml, text
Cc, Bcc (comma-separated)cc, bcc (string or array)
ReplyToreply_to
Headers: [{ Name, Value }]headers: { name: value }
Attachments: [{ Name, Content, ContentType }]attachments: [{ filename, content, contentType }]
POST /email/batchPOST /emails/batch, up to 100 emails
TemplateId / TemplateModel (/email/withTemplate)Render the HTML in your code, then send it as html
MessageStreamNo equivalent; Envello is for transactional mail only
Tag, MetadataNo equivalent yet

Templates

Postmark templates support Mustachio logic such as loops and conditionals. Envello's stored templates do {{variable}} substitution only. Simple templates port as they are. Anything with loops or conditionals is easier to render in your own code (React Email, MJML, any engine) and send as html.

Message Streams

Streams separate transactional from broadcast mail inside one Postmark account. Envello has no streams because it doesn't send broadcast mail: keep marketing email in a marketing tool and use Envello for transactional sends. If you need reputation isolation beyond that, dedicated IPs are a paid add-on.

Webhooks and inbound

Postmark bounce webhooks carry its own bounce-type names. Envello sends five event types (delivered, bounced, complained, opened, clicked), so map each Postmark type you branch on to one of those before writing the new handler. Inbound parsing is a separate feature: check what you use before assuming outbound migration covers it.

Four steps

The migration, end to end

1. Add Envello DNS records alongside your existing Postmark records

Verify your sending domain in Envello and add the SPF include, DKIM CNAMEs, and the return-path CNAME Envello generates. Keep your existing records in place: both providers can authenticate the same domain at once, which is what makes a safe cutover possible. Only remove the old records after Envello has been the sole sender for a few days.

2. Swap the sending call

Point your outbound path at Envello's SDK. The request shape (from, to, subject, html, text, cc, bcc, reply_to, headers, attachments) mirrors the common transactional-API pattern, so the diff in most codebases is the import line, the client instantiation, and the API key environment variable. Templates rendered client-side (React Email, MJML, plain string) plug straight into the html field.

3. Dual-send during the cutover window

Route a small percentage of traffic to Envello first, or fork production sends so each message goes through both providers to non-critical recipients while you verify. Envello's log search shows every send, bounce, complaint, open, and click for 90 days on Pro (7 days on Free, up to 365 days on Scale) so you can confirm parity before flipping the rest of the traffic.

4. Cut over and retire Postmark

Once Envello has handled a full week of production traffic without regressions on your key deliverability metrics (bounce rate, complaint rate, delivered rate per domain), route 100% of sends through Envello. Export your Postmark suppression list and add each address with Envello's POST /suppressions so previously-suppressed recipients stay suppressed. Remove the old DNS records last.

Sending HTML from React components? See the React Email guide for how render() plugs straight into the html field.

FAQ

Migration questions

Can I keep sending through Postmark while I test Envello?+

Yes. Both providers authenticate the same domain via separate DKIM selectors and their own SPF include, so parallel sending is safe as long as each provider's records are in place. Keep the old records until Envello has been the sole production sender for a few days, then remove them.

What happens to my suppression list?+

Export it from Postmark and add each address with POST /suppressions (a full-access API key works, one address per request), or one by one in the dashboard. If you skip this step, addresses that Postmark had marked as bounced or complained can send successfully through Envello on day one and generate the same bounce or complaint again, which harms deliverability. Import first, cut over second.

Do I need to change my webhook consumer?+

Yes, at least the payload shape. Envello's webhooks send delivered, bounced, complained, opened, and clicked events with HMAC-signed payloads; the field names and signature header are documented at envello.dev/docs. If your current consumer branches on your existing provider's specific bounce-subtype taxonomy, decide upfront how each subtype maps to Envello's simpler event set before writing the new handler.

How long does the whole migration take?+

For a codebase with a single central sending path, most teams complete the code change in under an hour. Add DNS propagation (up to 48 hours, usually much less), a dual-send verification window (a few days), and one week of production monitoring before retiring the old provider. Plan for two to three weeks end-to-end, not one afternoon.

Free tool

Check your own GDPR/DPA gaps before you switch

8 questions covering data residency, retention, and subprocessor documentation, the same things a security questionnaire asks.

Run the free checklist →
Ready to migrate?

Start free and migrate at your own pace.