Migrate from Postmark

A practical guide to moving your transactional email infrastructure from Postmark to Veil Mail, covering SDK changes, API mapping, and webhook migration.

Overview

Postmark is known for its focus on transactional email delivery. Veil Mail expands on this foundation with built-in PII scanning, CASL compliance, marketing campaigns, audience management, and a broader SDK ecosystem. If you're migrating from Postmark, the process is straightforward since both platforms share similar REST API patterns.

Key differences to note:

  • Postmark uses Server Tokens for authentication; Veil Mail uses scoped API keys (veil_live_ / veil_test_)
  • Postmark has a separate endpoint for template emails; Veil Mail uses the same /v1/emails endpoint with a templateId field
  • Postmark separates transactional and marketing into different "streams"; Veil Mail uses a type field on the email object
  • Veil Mail adds PII scanning, consent management, and automation that are not available in Postmark

Server Token to API Key Migration

Postmark uses Server Tokens (for sending) and Account Tokens (for management). Veil Mail consolidates these into a single API key that is scoped by environment and permissions.

Postmark

.env
// Postmark Server Token
X-Postmark-Server-Token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

// Postmark Account Token (for account-level API)
X-Postmark-Account-Token: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Veil Mail

.env
// Single API key scoped to environment
VEILMAIL_API_KEY=veil_live_xxxxx   # Production
# VEILMAIL_API_KEY=veil_test_xxxxx # Testing (no emails sent)

Note: Veil Mail API keys can be configured with granular permissions (send-only, read-only, full access) from the dashboard. You don't need separate tokens for different operations.

SDK Migration

Replace the postmark package with @resonia/veilmail-sdk.

# Remove Postmark
npm uninstall postmark

# Install Veil Mail
npm install @resonia/veilmail-sdk

Send Email Comparison

Postmark

postmark.ts
import { ServerClient } from 'postmark';

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

await client.sendEmail({
  From: 'hello@yourdomain.com',
  To: 'user@example.com',
  Subject: 'Welcome!',
  HtmlBody: '<h1>Welcome to our platform!</h1>',
  TextBody: 'Welcome to our platform!',
  MessageStream: 'outbound',
});

Veil Mail

veilmail.ts
import { VeilMail } from '@resonia/veilmail-sdk';

const client = new VeilMail('veil_live_xxxxx');

await client.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<h1>Welcome to our platform!</h1>',
  text: 'Welcome to our platform!',
});

Key difference: Postmark uses PascalCase field names (From, To, HtmlBody). Veil Mail uses camelCase (from, to, html), which is more idiomatic in JavaScript/TypeScript.

Template Email Comparison

Postmark

postmark-template.ts
// Postmark uses a separate method for template emails
await client.sendEmailWithTemplate({
  From: 'hello@yourdomain.com',
  To: 'user@example.com',
  TemplateAlias: 'welcome',
  TemplateModel: {
    firstName: 'Alice',
    orderNumber: '12345',
  },
  MessageStream: 'outbound',
});

Veil Mail

veilmail-template.ts
// Veil Mail uses the same send method with a templateId field
await client.emails.send({
  from: 'hello@yourdomain.com',
  to: 'user@example.com',
  templateId: 'template_xxxxx',
  templateData: {
    firstName: 'Alice',
    orderNumber: '12345',
  },
});

Batch Email Comparison

Postmark

postmark-batch.ts
await client.sendEmailBatch([
  {
    From: 'hello@yourdomain.com',
    To: 'user1@example.com',
    Subject: 'Hello User 1',
    HtmlBody: '<p>Welcome!</p>',
    MessageStream: 'outbound',
  },
  {
    From: 'hello@yourdomain.com',
    To: 'user2@example.com',
    Subject: 'Hello User 2',
    HtmlBody: '<p>Welcome!</p>',
    MessageStream: 'outbound',
  },
]);

Veil Mail

veilmail-batch.ts
await client.emails.sendBatch([
  {
    from: 'hello@yourdomain.com',
    to: 'user1@example.com',
    subject: 'Hello User 1',
    html: '<p>Welcome!</p>',
  },
  {
    from: 'hello@yourdomain.com',
    to: 'user2@example.com',
    subject: 'Hello User 2',
    html: '<p>Welcome!</p>',
  },
]);

API Endpoint Mapping

Postmark endpoints are hosted at https://api.postmarkapp.com. Veil Mail endpoints are at https://api.veilmail.xyz.

PostmarkVeil MailNotes
POST /emailPOST /v1/emailsSimilar payload, camelCase fields
POST /email/batchPOST /v1/emails/batchDirect mapping
GET /templatesGET /v1/templatesDirect mapping
POST /email/withTemplatePOST /v1/emailsUse templateId field
POST /email/batchWithTemplatesPOST /v1/emails/batchUse templateId field per email
GET /deliverystatsGET /v1/analytics/overviewRicher analytics response
GET /bouncesGET /v1/suppressionsUnified suppression list
GET /message-streams--Use type field instead

Simplified templates: Postmark has separate endpoints for sending with and without templates (/email vs /email/withTemplate). Veil Mail unifies these into a single POST /v1/emails endpoint -- just include templateId when you want to use a template.

Webhook Migration

Postmark webhooks use PascalCase event types. Veil Mail uses namespaced, lowercase event types.

Postmark EventVeil Mail Event
Deliveryemail.delivered
Bounceemail.bounced
SpamComplaintemail.complained
Openemail.opened
Clickemail.clicked
SubscriptionChangesubscriber.unsubscribed

Postmark includes a RecordType field in the payload. Veil Mail uses a top-level type field. Update your webhook handler accordingly:

webhook-migration.ts
// Before: Postmark webhook handler
app.post('/webhooks/postmark', (req, res) => {
  const { RecordType } = req.body;

  switch (RecordType) {
    case 'Delivery':
      handleDelivery(req.body);
      break;
    case 'Bounce':
      handleBounce(req.body);
      break;
    case 'SpamComplaint':
      handleComplaint(req.body);
      break;
  }

  res.sendStatus(200);
});

// After: Veil Mail webhook handler
app.post('/webhooks/veilmail', (req, res) => {
  // Verify signature
  const signature = req.headers['x-veilmail-signature'] as string;
  // ... verify with HMAC-SHA256

  const { type, data } = req.body;

  switch (type) {
    case 'email.delivered':
      handleDelivery(data);
      break;
    case 'email.bounced':
      handleBounce(data);
      break;
    case 'email.complained':
      handleComplaint(data);
      break;
  }

  res.json({ received: true });
});

Important: Postmark does not require webhook signature verification. Veil Mail includes an X-VeilMail-Signature header on every webhook delivery and we strongly recommend verifying it to prevent spoofed events.

What You Gain

Postmark is excellent for transactional email. Veil Mail gives you everything Postmark offers plus a comprehensive marketing and compliance toolkit.

  • Automatic PII protection -- every email is scanned for sensitive data (SSNs, credit cards, health records) before delivery
  • Built-in CASL compliance -- consent tracking, unsubscribe management, and physical address enforcement
  • Marketing campaigns -- Postmark's broadcast streams are limited compared to Veil Mail's full campaign builder with A/B testing and scheduling
  • Audience management -- first-class subscriber management with custom properties, segments, and signup forms
  • Automation sequences -- build drip campaigns and multi-step email workflows with branching logic
  • SDKs for every language -- official SDKs for Node.js, Python, Go, Ruby, PHP, Java, .NET, Rust, Elixir, and more
  • Unified API -- no separate endpoints for template vs. non-template sends, and no message streams to manage