Postmark Notification Plugin
Comprehensive Postmark email integration for Medusa with PDF attachments, templates, upsell campaigns, and abandoned cart recovery
Overview
The medusa-plugin-postmark is a comprehensive notification plugin for Medusa that sends transactional emails through Postmark’s service. This plugin provides advanced features including email templating, PDF attachment generation, upsell campaigns, and abandoned cart recovery sequences.
Key Features
- Email Templating - Leverage Postmark’s built-in template system
- PDF Attachments - Generate and attach invoices and credit notes using pdfkit
- Upsell Campaigns - Send targeted emails to customers based on product collections
- Abandoned Cart Emails - Automated multi-stage cart recovery sequence
- Designer-Friendly - Compatible with tools like stripo.email for template creation
- Multi-Language Support - Define templates per language
- Event Triggers - Support for order, customer, user, and auth events
System Requirements
Before installing the plugin, ensure your system meets the following requirements:
- Medusa server: v2.x
- Node.js: v20 or later
- Postmark Account: Active Postmark account with verified sender address
Installation
Install the plugin using your preferred package manager:
# Using npm
npm install medusa-plugin-postmark
Configuration
Add the plugin to your medusa-config.js:
const plugins = [
{
resolve: `medusa-plugin-postmark`,
options: {
server_api: process.env.POSTMARK_SERVER_API,
from: process.env.POSTMARK_FROM,
bcc: process.env.POSTMARK_BCC || null,
pdf: {
enabled: process.env.POSTMARK_PDF_ENABLED || false,
settings: {
font: process.env.POSTMARK_PDF_FONT || 'Helvetica',
format: process.env.POSTMARK_PDF_FORMAT || 'A4',
margin: { top: '50', right: '50', bottom: '50', left: '50' },
empty: ""
},
header: { enabled: false, content: null, height: '50' },
footer: { enabled: false, content: null },
templates: {
invoice: process.env.POSTMARK_PDF_INVOICE_TEMPLATE || null,
credit_note: process.env.POSTMARK_PDF_CREDIT_NOTE_TEMPLATE || null,
return_invoice: process.env.POSTMARK_PDF_RETURN_INVOICE_TEMPLATE || null
}
},
events: {
order: {
placed: process.env.POSTMARK_ORDER_PLACED || null,
canceled: process.env.POSTMARK_ORDER_CANCELED || null,
shipment_created: process.env.POSTMARK_ORDER_SHIPMENT_CREATED || null
},
customer: {
created: process.env.POSTMARK_CUSTOMER_CREATED || null,
password_reset: process.env.POSTMARK_CUSTOMER_PASSWORD_RESET || null
},
user: {
created: process.env.POSTMARK_USER_CREATED || null,
password_reset: process.env.POSTMARK_USER_PASSWORD_RESET || null
},
auth: {
password_reset: process.env.POSTMARK_AUTH_PASSWORD_RESET || null,
verify_account: process.env.POSTMARK_AUTH_VERIFY_ACCOUNT || null
},
activity: {
inactive_user: process.env.POSTMARK_ACTIVITY_INACTIVE_USER || null,
inactive_customer: process.env.POSTMARK_ACTIVITY_INACTIVE_CUSTOMER || null
}
},
upsell: {
enabled: process.env.POSTMARK_UPSELL_ENABLED || false,
template: process.env.POSTMARK_UPSELL_TEMPLATE || null,
delay: process.env.POSTMARK_UPSELL_DELAY || 9,
valid: process.env.POSTMARK_UPSELL_VALID || 30,
collection: process.env.POSTMARK_UPSELL_COLLECTION || null
},
abandoned_cart: {
enabled: process.env.POSTMARK_ABANDONED_CART_ENABLED || false,
first: { delay: 1, template: null },
second: { delay: 24, template: null },
third: { delay: 48, template: null }
},
default_data: {
product_url: process.env.POSTMARK_PRODUCT_URL || '',
product_name: process.env.POSTMARK_PRODUCT_NAME || '',
company_name: process.env.POSTMARK_COMPANY_NAME || '',
company_address: process.env.POSTMARK_COMPANY_ADDRESS || ''
}
}
}
]
Environment Variables
Add the following to your .env file:
# Required
POSTMARK_SERVER_API=your_postmark_api_key
POSTMARK_FROM=noreply@yourdomain.com
# Optional
POSTMARK_BCC=admin@yourdomain.com
# PDF Settings
POSTMARK_PDF_ENABLED=true
POSTMARK_PDF_FONT=Helvetica
POSTMARK_PDF_FORMAT=A4
# Event Templates (Postmark Template IDs)
POSTMARK_ORDER_PLACED=1234567
POSTMARK_ORDER_CANCELED=1234568
POSTMARK_CUSTOMER_CREATED=1234569
# Upsell Campaign
POSTMARK_UPSELL_ENABLED=true
POSTMARK_UPSELL_TEMPLATE=1234570
POSTMARK_UPSELL_DELAY=9
POSTMARK_UPSELL_COLLECTION=collection_id
# Abandoned Cart
POSTMARK_ABANDONED_CART_ENABLED=true
# Company Info
POSTMARK_COMPANY_NAME=Your Company Name
POSTMARK_COMPANY_ADDRESS=123 Main St, City, State
POSTMARK_PRODUCT_URL=https://yourstore.com
PDF Template System
Templates use JSON structure with pdfkit for document generation. Six primary element types available:
Image Element
Place local images in /src/images/:
{
"type": "image",
"image": "image.png",
"x": 100,
"y": 100,
"fit": [200, 50]
}
Text Element
{
"type": "text",
"text": "Sample content",
"size": 12,
"color": "#000000",
"align": "left"
}
MoveDown Element
{
"type": "moveDown",
"lines": 2
}
HR Element
{
"type": "hr",
"color": "#cccccc"
}
Table Row
{
"type": "tableRow",
"columns": [
{ "text": "Header 1", "width": 200 },
{ "text": "Header 2", "width": 150 }
]
}
Item Loops
{ "type": "itemLoop" }
Template Variables
Access dynamic data using {{ variable_name }} syntax. Nested properties work: {{ order.customer.first_name }}.
Variable Functions
Dates:
{{ order.placed_at | date('en-US',{'year': 'numeric', 'month': 'long', 'day': 'numeric'}) }}
Currency:
{{ order.total_price | currency('en-US') }}
Country codes:
{{ order.shipping_address.country_code | country }}
Conditional Statements
{{ if variable }}
Content to show if true
{{ endif }}
{{ if not variable }}
Content to show if false
{{ endif }}
Multi-Language Support
Define templates per language in configuration:
events: {
order: {
placed: { nl: 1234, en: 1235 }
}
}
Abandoned Cart Recovery
Configure a three-stage abandoned cart email sequence:
abandoned_cart: {
enabled: true,
first: { delay: 1, template: 'template_id_1' }, // 1 hour
second: { delay: 24, template: 'template_id_2' }, // 24 hours
third: { delay: 48, template: 'template_id_3' } // 48 hours
}
Upsell Campaigns
Target customers with product recommendations:
upsell: {
enabled: true,
template: 'upsell_template_id',
delay: 9, // Days after purchase
valid: 30, // Campaign validity in days
collection: 'col_id' // Product collection to promote
}