npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@const-code/medusa-notification-postmark

v1.0.1

Published

A Postmark notification provider for Medusa v2. Supports custom HTML and Postmark's hosted templates.

Downloads

13

Readme

@const-code/medusa-notification-postmark

A Postmark notification provider for Medusa v2. Supports sending emails using custom HTML (React Email, Handlebars, plain HTML) or Postmark's hosted templates.

Features

  • Dual Mode - Use custom HTML or Postmark's hosted templates
  • Auto CID Conversion - Automatically converts data:image URLs to email attachments
  • Message Streams - Supports transactional, broadcasts, and custom streams
  • Full Postmark API - Tracking, tags, metadata, and custom headers
  • TypeScript - Fully typed for type safety
  • Zero Templates - Bring your own templates or use Postmark's
  • Framework Agnostic - Works with any rendering engine

Two Ways to Send Emails

This provider gives you full flexibility in how you design and send your emails:

1. Custom HTML Templates

Build your email templates in code using React Email, Handlebars, or plain HTML. Perfect for transactional emails where you need full control and version control.

2. Postmark Templates

Use Postmark's template editor to design and customize emails directly in the Postmark dashboard. No code changes needed - fully customizable via Postmark's interface. Ideal for marketing emails and when non-technical team members need to edit templates.

Installation

npm install @const-code/medusa-notification-postmark postmark

Or with yarn:

yarn add @const-code/medusa-notification-postmark postmark

Quick Start

1. Configure in medusa-config.ts

import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  modules: [
    {
      resolve: "@const-code/medusa-notification-postmark",
      options: {
        serverToken: process.env.POSTMARK_SERVER_TOKEN,
        from: "[email protected]",
        messageStream: "outbound", // Optional
        trackOpens: true, // Optional
      },
    },
  ],
})

2. Add Environment Variables

POSTMARK_SERVER_TOKEN=your-server-token-here

3. Use in Subscribers

Option A: Custom HTML (React Email)

import { render } from "@react-email/components"
import { OrderEmail } from "../emails/order"

export default async function({ event, container }) {
  const notificationService = container.resolve("notification")
  const order = event.data
  
  // Render your template
  const html = await render(<OrderEmail order={order} />)
  
  // Send via Postmark
  await notificationService.createNotifications({
    to: order.email,
    channel: "email",
    template: "order-placed",
    data: {
      subject: `Order #${order.display_id} Confirmed`,
      html: html,
    },
  })
}

Option B: Postmark Templates

export default async function({ event, container }) {
  const notificationService = container.resolve("notification")
  const user = event.data
  
  // Use Postmark template from dashboard
  await notificationService.createNotifications({
    to: user.email,
    channel: "email",
    template: "welcome",
    data: {
      templateAlias: "welcome-email", // or templateId: 12345
      templateModel: {
        user_name: user.name,
        login_url: "https://yourstore.com/login",
      },
    },
  })
}

Configuration Options

Provider Options (medusa-config.ts)

Configure these when registering the provider:

| Option | Type | Required | Default | Description | |--------|------|----------|---------|-------------| | serverToken | string | Yes | - | Your Postmark server API token | | from | string | Yes | - | Default "from" email address | | messageStream | string | No | "outbound" | Default message stream | | convertDataUrlsToCID | boolean | No | true | Auto-convert data URLs to CID attachments | | trackOpens | boolean | No | undefined | Track email opens by default | | trackLinks | string | No | undefined | Track link clicks: "None", "HtmlAndText", "HtmlOnly", "TextOnly" |


Notification Data Options (data field)

These are passed in the data object when calling createNotifications():

Mode 1: Custom HTML

| Field | Type | Required | Description | |-------|------|----------|-------------| | html | string | Yes | Rendered HTML content | | subject | string | Yes | Email subject line |

Mode 2: Postmark Templates

| Field | Type | Required | Description | |-------|------|----------|-------------| | templateId | number | One required | Postmark template ID | | templateAlias | string | One required | Postmark template alias | | templateModel | object | Yes | Variables for the template | | subject | string | No | Optional subject override |

Common Options (Both Modes)

| Field | Type | Description | Example | |-------|------|-------------|---------| | from | string | Override default from address | "[email protected]" | | replyTo | string | Reply-to address | "[email protected]" | | cc | string \| string[] | CC recipients | "[email protected]" | | bcc | string \| string[] | BCC recipients | ["[email protected]", "[email protected]"] | | tag | string | Tag for categorization in Postmark | "order-confirmation" | | metadata | Record<string, string> | Custom key-value metadata | { orderId: "123" } | | attachments | Attachment[] | File attachments | See Attachment type below | | messageStream | string | Override message stream | "broadcasts" | | trackOpens | boolean | Override open tracking | true | | trackLinks | string | Override link tracking | "HtmlAndText" | | headers | Array<{Name, Value}> | Custom email headers | [{ Name: "X-Custom", Value: "test" }] |

Attachment Type

{
  Name: string,        // Filename (e.g., "invoice.pdf")
  Content: string,     // Base64-encoded file content
  ContentType: string, // MIME type (e.g., "application/pdf")
  ContentID?: string,  // For inline images (e.g., "cid:logo")
}

Examples

Example 1: QR Codes (Auto-converted to CID)

import QRCode from "qrcode"

const qrCode = await QRCode.toDataURL(ticketId)
const html = await render(<TicketEmail qrCode={qrCode} />)

// Provider automatically converts data:image to CID!
await notificationService.createNotifications({
  to: email,
  channel: "email",
  template: "ticket",
  data: {
    subject: "Your Ticket",
    html: html,
  },
})

Example 2: Message Streams

// Transactional email
await notificationService.createNotifications({
  to: email,
  channel: "email",
  template: "receipt",
  data: {
    subject: "Your Receipt",
    html: html,
    messageStream: "outbound", // Default
  },
})

// Marketing email
await notificationService.createNotifications({
  to: email,
  channel: "email",
  template: "newsletter",
  data: {
    subject: "Monthly Newsletter",
    html: html,
    messageStream: "broadcasts",
  },
})

Example 3: Attachments

await notificationService.createNotifications({
  to: email,
  channel: "email",
  template: "invoice",
  data: {
    subject: "Your Invoice",
    html: html,
    attachments: [
      {
        Name: "invoice.pdf",
        Content: "base64-content-here",
        ContentType: "application/pdf",
      },
    ],
  },
})

Example 4: Metadata & Tags

await notificationService.createNotifications({
  to: email,
  channel: "email",
  template: "order",
  data: {
    subject: "Order Confirmed",
    html: html,
    tag: "order-confirmation",
    metadata: {
      orderId: order.id,
      customerId: customer.id,
    },
  },
})

Message Streams

| Stream | Purpose | Example Use Cases | |--------|---------|-------------------| | outbound | Transactional emails (default) | Orders, receipts, password resets | | broadcasts | Marketing emails | Newsletters, promotions | | Custom | Any stream you create in Postmark | VIP customers, internal alerts |

TypeScript Support

All TypeScript types are exported for autocomplete and type safety:

import type {
  PostmarkOptions,           // Provider configuration options
  PostmarkNotificationData,  // What you pass in 'data' field
  NotificationPayload,       // Full notification payload structure
  Attachment,                // File attachment type
} from "@const-code/medusa-notification-postmark"

// Utility functions
import { convertDataUrlsToCID } from "@const-code/medusa-notification-postmark"

Example with Types:

import type { PostmarkNotificationData } from "@const-code/medusa-notification-postmark"

// Your IDE will now show all available options!
const emailData: PostmarkNotificationData = {
  subject: "Order Confirmed",
  html: renderedHtml,
  tag: "order-confirmation",
  trackOpens: true,
  metadata: {
    orderId: "123",
    customerId: "456",
  },
}

await notificationService.createNotifications({
  to: customer.email,
  channel: "email",
  template: "order",
  data: emailData,
})

Utility Functions

convertDataUrlsToCID

Manually convert data URLs to CID attachments:

import { convertDataUrlsToCID } from "@const-code/medusa-notification-postmark"

const html = `<img src="data:image/png;base64,..." />`
const { html: finalHtml, attachments } = convertDataUrlsToCID(html)

// finalHtml: <img src="cid:image-0" />
// attachments: [{ Name: "image-0.png", Content: "...", ContentID: "cid:image-0" }]

When to Use Each Approach

Custom HTML

  • Full control over design
  • Version controlled with your code
  • TypeScript type safety
  • Dynamic logic in templates

Best for: Order confirmations, receipts, password resets

Postmark Templates

  • Non-technical team can edit templates
  • A/B testing in Postmark dashboard
  • No deployment needed for updates
  • Multi-language support

Best for: Marketing emails, newsletters, campaigns

FAQ

Q: Do I need to include templates in this package?
A: No! This is a transport-only provider. You bring your own templates or use Postmark's.

Q: Can I use both approaches in the same project?
A: Yes! Use custom HTML for transactional and Postmark templates for marketing.

Q: Are QR codes supported?
A: Yes! Any data:image URL is automatically converted to CID attachments.

Q: Can I disable CID conversion?
A: Yes, set convertDataUrlsToCID: false in options.

License

MIT

Contributing

Contributions are welcome. Please open an issue or pull request.

Links