postmark-send-email
Skill46 starsChanged 55 days agoMITView on GitHub
Use when sending transactional or broadcast emails through Postmark — single sends, batch (up to 500), bulk, or template-based emails with support for attachments, tracking, and message streams.
- Reads credentials
In this repository5 other files it gives its agents.
- postmark-email-best-practicesSkill
- postmark-inboundSkill
- postmark-templatesSkill
- postmark-webhooksSkill
- postmarkSkill
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 36
Never mix transactional and broadcast in the same stream — it damages deliverability.
- Distinctive: line 47
All API requests require the Server API Token:
- Very specific: line 144
Use server-side Handlebars templates — no client-side rendering needed.
- Distinctive: line 221
Never: test with fake addresses at real providers (e.g., test@gmail.com) — damages sender reputation.
- Very specific: line 277
Use POSTMARK_API_TEST as the token value in development and CI — validates without sending
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Send Email with Postmark
Overview
Postmark provides multiple endpoints for sending emails:
| Approach | Endpoint | Use Case | Limits |
| Single | POST /email | Individual transactional emails | 1 email, 10 MB payload including attachments |
| Batch | POST /email/batch | Up to 500 emails in one request | 500 emails, 50 MB payload including attachments |
| Template | POST /email/withTemplate | Dynamic content with server-side templates | 1 email, 10 MB payload including attachments |
| Batch Template | POST /email/batchWithTemplates | Bulk templated emails | 500 emails, 50 MB payload including attachments |
| Bulk | POST /email/bulk | Broadcast stream campaigns | No fixed recipient cap, 50 MB payload including attachments |
Choose batch when: sending 2+ distinct emails at once, performance matters, or attachments are needed. Choose single when: sending one email or real-time per-message error handling is needed.
Message Streams (CRITICAL)
Postmark separates emails by intent. Always specify MessageStream:
| Stream | Value | Purpose | SMTP Endpoint |
| Transactional | outbound | 1:1 triggered emails (default) | smtp.postmarkapp.com |
| Broadcast | broadcast | Marketing, newsletters | smtp-broadcasts.postmarkapp.com |
Never mix transactional and broadcast in the same stream — it damages deliverability. Servers can have up to 10 message streams.
Quick Start
- Get API Token from your Postmark server settings
- Verify sender domain or email address
- Install SDK — see references/installation.md
- Choose endpoint based on the decision matrix above
Authentication
All API requests require the Server API Token:
X-Postmark-Server-Token: your-server-token-hereStore the token in POSTMARK_SERVER_TOKEN. For testing without sending, use POSTMARK_API_TEST as the token value.
Single Email
Endpoint: POST https://api.postmarkapp.com/email
Required Parameters
| Parameter | Type | Description |
From | string | Sender address (must be a verified domain or sender signature) |
To | string | Recipients (comma-separated, max 50 total with Cc/Bcc) |
Subject | string | Email subject line |
TextBody or HtmlBody | string | Message content (at least one required) |
Optional Parameters
| Parameter | Type | Description |
Cc | string | CC recipients |
Bcc | string | BCC recipients |
ReplyTo | string | Reply-to address |
MessageStream | string | outbound (default) or broadcast |
Tag | string | Category for statistics (one per message, max 1000 chars) |
Metadata | object | Key-value pairs for custom tracking data (returned in webhook payloads) |
TrackOpens | boolean | Enable open tracking |
TrackLinks | string | None, HtmlAndText, HtmlOnly, TextOnly |
Headers | array | Custom email headers [{Name, Value}] |
Attachments | array | File attachments (max 10 MB total) |
const postmark = require('postmark');
const client = new postmark.ServerClient(process.env.POSTMARK_SERVER_TOKEN);
const result = await client.sendEmail({
From: 'notifications@yourdomain.com',
To: 'customer@example.com',
Subject: 'Your order has shipped',
TextBody: 'Your order #12345 is on its way!',
HtmlBody: '<p>Your order <strong>#12345</strong> is on its way!</p>',
MessageStream: 'outbound',
Tag: 'order-shipped'
});
console.log('MessageID:', result.MessageID);Response includes MessageID — use it for tracking via webhooks or the Messages API.
See references/single-email-examples.md for Python, Ruby, PHP, .NET, and cURL examples.
Batch Email
Endpoint: POST https://api.postmarkapp.com/email/batch
Send up to 500 emails in a single API call. Each message is independently validated and can have its own attachments, tags, and metadata.
const results = await client.sendEmailBatch([
{
From: 'sender@yourdomain.com',
To: 'user1@example.com',
Subject: 'Order Shipped',
TextBody: 'Your order has shipped!',
MessageStream: 'outbound'
},
{
From: 'sender@yourdomain.com',
To: 'user2@example.com',
Subject: 'Order Confirmed',
TextBody: 'Your order is confirmed!',
MessageStream: 'outbound'
}
]);
// Check individual results — always handle partial failures
results.forEach((result, index) => {
if (result.ErrorCode === 0) {
console.log(`Email ${index + 1} sent: ${result.MessageID}`);
} else {
console.error(`Email ${index + 1} failed: ${result.Message}`);
}
});For more than 500 emails, chunk the array into groups of 500 and send sequentially. See references/batch-email-examples.md for chunking patterns, attachments, and Python/Ruby/cURL examples.
Send with Template
Endpoint: POST https://api.postmarkapp.com/email/withTemplate
Use server-side Handlebars templates — no client-side rendering needed. Always use TemplateAlias over TemplateId — aliases survive re-creation and work across environments.
const result = await client.sendEmailWithTemplate({
From: 'sender@yourdomain.com',
To: 'customer@example.com',
TemplateAlias: 'order-confirmation',
TemplateModel: {
customer_name: 'Jane Doe',
order_number: 'ORD-67890',
items: [
{ name: 'Widget', price: '$19.99' },
{ name: 'Gadget', price: '$29.99' }
]
},
MessageStream: 'outbound'
});For batch template sends (up to 500), use POST /email/batchWithTemplates via client.sendEmailBatchWithTemplates([...]). See references/template-examples.md for full examples.
Attachments
Include attachments as Base64-encoded content:
{
"Attachments": [
{
"Name": "invoice.pdf",
"Content": "base64-encoded-content-here",
"ContentType": "application/pdf"
}
]
}Embed inline images using ContentID:
{
"HtmlBody": "<img src=\"cid:logo123\">",
"Attachments": [
{
"Name": "logo.png",
"Content": "base64-encoded-image",
"ContentType": "image/png",
"ContentID": "cid:logo123"
}
]
}Size limits: TextBody/HtmlBody 5 MB each · Single message with attachments 10 MB · Batch payload 50 MB · Base64 encoding adds ~33% · Certain file types blocked (.exe, .bat)
Tracking
Configure per-email or at server level:
{ "TrackOpens": true, "TrackLinks": "HtmlAndText" }TrackLinks options: None | HtmlAndText | HtmlOnly | TextOnly
Disable tracking for sensitive transactional emails (password resets, security alerts) to maximize deliverability.
Testing
| Method | Address/Token | Result |
| API Test Token | POSTMARK_API_TEST | Validates request without sending |
| Black Hole | test@blackhole.postmarkapp.com | Dropped but appears in activity |
| Sandbox Server | Create sandbox server in dashboard | Full processing, no delivery |
| Hard Bounce | hardbounce@bounce-testing.postmarkapp.com | Simulates hard bounce |
| Soft Bounce | softbounce@bounce-testing.postmarkapp.com | Simulates soft bounce |
Never test with fake addresses at real providers (e.g., test@gmail.com) — damages sender reputation.
Error Handling
| Code | Meaning | Action |
| 200 | Success | Continue |
| 401 | Unauthorized | Check API token — do not retry |
| 406 | Inactive recipient | Check suppression list — do not retry |
| 409 | JSON required | Fix Accept/Content-Type headers |
| 410 | Too many batch messages | Reduce to 500 or fewer per batch |
| 413 | Payload too large | Reduce payload (10 MB single, 50 MB batch) |
| 422 | Validation error | Fix request parameters — do not retry |
| 429 | Rate limited | Retry with exponential backoff |
| 500 | Server error | Retry with exponential backoff |
async function sendWithRetry(client, email, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await client.sendEmail(email);
} catch (error) {
const isRetryable = error.statusCode === 429 || error.statusCode === 500;
if (!isRetryable || attempt === maxRetries) throw error;
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
}
}
}See references/error-handling.md for complete error patterns including batch partial failures and typed error classes.
SMTP
Postmark supports SMTP for legacy integrations — host smtp.postmarkapp.com (transactional) or smtp-broadcasts.postmarkapp.com (broadcast), ports 25/2525/587, Server API Token as username and password. See references/smtp-migration.md for migration examples and custom header reference.
Common Mistakes
| Mistake | Fix |
| Missing MessageStream | Always specify outbound or broadcast |
| Using broadcast for transactional | Use separate streams for different email types |
| Testing with real addresses | Use POSTMARK_API_TEST or sandbox mode |
| Retrying 422 errors | These are validation errors — fix request, don't retry |
| Not handling partial batch failures | Check each result in batch response array |
| Tracking on sensitive transactional emails | Disable for password resets, security alerts, receipts |
| Exceeding 50 recipients per email | Split into multiple emails or use batch |
| Not verifying sender | Domain or address must be verified before sending |
Notes
Fromaddress must use a verified domain or sender signature- Store API key in
POSTMARK_SERVER_TOKENenvironment variable - Maximum 50 recipients total per email (To + Cc + Bcc)
MessageIDreturned in response is used for bounce/webhook/API correlation- For broadcast campaigns to large lists, use the Bulk API endpoint (
POST /email/bulk) - Use
POSTMARK_API_TESTas the token value in development and CI — validates without sending - New domains require gradual volume warm-up — see
postmark-email-best-practicesfor the schedule
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

