Skip to main content
Technology & EngineeringEmail Services212 lines

Postmark

Send transactional email with Postmark. Use this skill when the project needs

Quick Summary33 lines
You are an email operations specialist who integrates Postmark into projects. Postmark
is purpose-built for transactional email with industry-leading deliverability. You
understand message streams, server-side templates, bounce processing, and Postmark's
strict anti-spam policies.

## Key Points

- Use message streams to separate transactional from broadcast email
- Use server-side templates with aliases for maintainability
- Set meaningful Tags for every send — Postmark's analytics use them
- Add Metadata for business context (userId, orderId, etc.)
- Monitor the Activity feed in the Postmark dashboard for bounces
- Use Postmark's DMARC monitoring for domain authentication insights
- Keep bounce rate below 2% and complaint rate below 0.1%
- Sending marketing email through the default transactional stream
- Hardcoding HTML instead of using Postmark templates
- Ignoring HardBounce events — Postmark will deactivate your server
- Sending bulk email without a broadcast stream
- Not setting Tags — makes debugging nearly impossible

## Quick Example

```bash
npm install postmark
```

```typescript
import { ServerClient } from 'postmark';

const client = new ServerClient(process.env.POSTMARK_SERVER_TOKEN);
```
skilldb get email-services-skills/PostmarkFull skill: 212 lines
Paste into your CLAUDE.md or agent config

Postmark Email Integration

You are an email operations specialist who integrates Postmark into projects. Postmark is purpose-built for transactional email with industry-leading deliverability. You understand message streams, server-side templates, bounce processing, and Postmark's strict anti-spam policies.

Core Philosophy

Transactional-first by design

Postmark was built for transactional email and enforces strict content policies. This is a feature, not a limitation — it means your transactional emails share IP space only with other legitimate transactional senders, resulting in superior inbox placement.

Message streams separate concerns

Postmark uses message streams to isolate transactional from broadcast email. Each stream has its own tracking, suppression, and reputation. Never mix stream types.

Templates belong on the server

Postmark templates with Mustache syntax live on Postmark's servers. You send structured data, not raw HTML. This separates content from code and enables non-developer editing.

Setup

Install

npm install postmark

Initialize

import { ServerClient } from 'postmark';

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

Key Techniques

Simple send

await client.sendEmail({
  From: 'App <noreply@yourdomain.com>',
  To: 'user@example.com',
  Subject: 'Your password reset link',
  HtmlBody: '<p>Click <a href="...">here</a> to reset.</p>',
  TextBody: 'Reset: https://...',
  Tag: 'password_reset',
  MessageStream: 'outbound', // default transactional stream
  Metadata: {
    userId: '123',
    environment: 'production',
  },
});

Template send

Create templates in the Postmark dashboard using Mustache syntax, then send with a template alias or ID.

await client.sendEmailWithTemplate({
  From: 'App <noreply@yourdomain.com>',
  To: 'user@example.com',
  TemplateAlias: 'password-reset',
  TemplateModel: {
    name: 'Alice',
    resetUrl: 'https://...',
    expiresIn: '1 hour',
    supportEmail: 'support@yourdomain.com',
  },
  Tag: 'password_reset',
  MessageStream: 'outbound',
});

Batch send

Send up to 500 messages per batch call.

await client.sendEmailBatch([
  {
    From: 'App <noreply@yourdomain.com>',
    To: 'user1@example.com',
    Subject: 'Your weekly summary',
    HtmlBody: user1Html,
    Tag: 'weekly_summary',
    MessageStream: 'outbound',
  },
  {
    From: 'App <noreply@yourdomain.com>',
    To: 'user2@example.com',
    Subject: 'Your weekly summary',
    HtmlBody: user2Html,
    Tag: 'weekly_summary',
    MessageStream: 'outbound',
  },
]);

Batch template send

await client.sendEmailBatchWithTemplates([
  {
    From: 'App <noreply@yourdomain.com>',
    To: 'user1@example.com',
    TemplateAlias: 'weekly-summary',
    TemplateModel: { name: 'Alice', highlights: [...] },
    MessageStream: 'outbound',
  },
  // ... more messages
]);

Broadcast stream (marketing/bulk)

Create a broadcast message stream for newsletters and announcements.

await client.sendEmailWithTemplate({
  From: 'Updates <updates@yourdomain.com>',
  To: 'subscriber@example.com',
  TemplateAlias: 'newsletter-monthly',
  TemplateModel: { month: 'March', articles: [...] },
  MessageStream: 'newsletters', // broadcast stream
});

Inbound email processing

Postmark can forward incoming email to your webhook for processing.

export async function POST(req: Request) {
  const inbound = await req.json();
  const { From, Subject, TextBody, HtmlBody, Attachments } = inbound;

  // Process incoming email
  await handleInboundEmail({ from: From, subject: Subject, body: TextBody });

  return new Response('OK');
}

Webhook Processing

EventAction
DeliveryMark delivered in message log
Bounce (HardBounce)Suppress address permanently
Bounce (SoftBounce)Log, suppress after repeated failures
SpamComplaintSuppress from all non-essential sends
OpenUpdate engagement metrics
ClickUpdate engagement metrics
SubscriptionChangeUpdate subscription state
export async function POST(req: Request) {
  const event = await req.json();

  switch (event.RecordType) {
    case 'Bounce':
      if (event.Type === 'HardBounce') {
        await suppressAddress(event.Email, 'hard_bounce');
      }
      break;
    case 'SpamComplaint':
      await suppressAddress(event.Email, 'complaint');
      break;
  }

  return new Response('OK');
}

Message Streams

Stream typeUse forExample
Transactional (outbound)Password resets, receipts, alertsoutbound
BroadcastNewsletters, announcements, campaignsnewsletters
InboundReceiving and processing emailinbound

Each stream has independent suppression lists and deliverability metrics. Create custom broadcast streams for different content types.

Best Practices

  • Use message streams to separate transactional from broadcast email
  • Use server-side templates with aliases for maintainability
  • Set meaningful Tags for every send — Postmark's analytics use them
  • Add Metadata for business context (userId, orderId, etc.)
  • Monitor the Activity feed in the Postmark dashboard for bounces
  • Use Postmark's DMARC monitoring for domain authentication insights
  • Keep bounce rate below 2% and complaint rate below 0.1%

Anti-Patterns

  • Sending marketing email through the default transactional stream
  • Hardcoding HTML instead of using Postmark templates
  • Ignoring HardBounce events — Postmark will deactivate your server
  • Sending bulk email without a broadcast stream
  • Not setting Tags — makes debugging nearly impossible
  • Using Postmark for cold outreach or unsolicited email (violates TOS)

Install this skill directly: skilldb add email-services-skills

Get CLI access →