postmark-inbound/SKILL.md
Use when processing incoming emails with Postmark inbound webhooks — building reply-by-email, email-to-ticket, document extraction, or any workflow that receives and parses email.
npx skillsauth add activecampaign/postmark-skills postmark-inboundInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Postmark's inbound processing parses incoming emails and delivers them as structured JSON to your webhook endpoint. This enables workflows like:
Sender → Email → Postmark → Parses email → POST JSON → Your webhook endpoint
If your endpoint returns a non-200 status code, Postmark will automatically retry delivery up to 10 times over approximately 10.5 hours with escalating intervals:
| Retry | Interval After Previous Attempt | |-------|-------------------------------| | 1 | 1 minute | | 2 | 5 minutes | | 3 | 10 minutes | | 4 | 10 minutes | | 5 | 10 minutes | | 6 | 15 minutes | | 7 | 30 minutes | | 8 | 1 hour | | 9 | 2 hours | | 10 | 6 hours |
Important: A 403 response immediately stops all retries — Postmark interprets this as intentional rejection. After all retries are exhausted, the message is marked as "Failed" and appears as an "Inbound Error" in your activity page. You can manually retry failed messages via the API (PUT /messages/inbound/{messageid}/retry).
Two setup options — MX record (recommended) or email forwarding. Constraints: one inbound stream per server, one domain per stream, one webhook URL per stream.
See references/inbound-setup.md for full DNS steps, forwarding caveats, retry schedule, and how to set your webhook URL.
Key fields in the JSON Postmark POSTs to your endpoint:
| Field | Description |
|-------|-------------|
| From | Sender email address |
| Subject | Email subject line |
| MailboxHash | The + hash from the recipient address — primary routing mechanism |
| TextBody | Full plain text body (includes quoted replies) |
| StrippedTextReply | Reply text only — quoted content stripped |
| HtmlBody | Full HTML body |
| Attachments | Array of {Name, Content, ContentType, ContentLength, ContentID} |
| MessageID | Unique Postmark message identifier |
| Headers | All email headers as [{Name, Value}] |
See references/payload-structure.md for the full payload JSON, attachment fields, and header threading examples.
Use + addressing to route emails to specific records or conversations:
[email protected] → MailboxHash: "ticket-456"
[email protected] → MailboxHash: "order-789"
This is the primary mechanism for threading replies back to conversations or routing to specific records.
const express = require('express');
const app = express();
app.use(express.json({ limit: '50mb' }));
app.post('/webhooks/inbound', (req, res) => {
const { From, Subject, MailboxHash, StrippedTextReply, TextBody } = req.body;
if (MailboxHash) {
// Threaded reply — parse the hash to find the related record
const [type, id] = MailboxHash.split('-');
console.log(`Reply for ${type} #${id} from ${From}`);
} else {
console.log(`New inbound email from ${From}: ${Subject}`);
}
// Always prefer StrippedTextReply for replies
const replyText = StrippedTextReply || TextBody;
res.sendStatus(200); // Must return 200
});
See references/handler-examples.md for Node.js, Python, attachment processing, reply-by-email, and async processing patterns.
Block unwanted senders by address or domain, and query/retry processed messages via the API.
See references/inbound-api.md for inbound rules endpoints and the Messages API.
| Mistake | Fix |
|---------|-----|
| Not returning HTTP 200 | Always respond 200 — even if you process asynchronously |
| Returning 403 accidentally | This permanently stops retries for that message |
| Not parsing MailboxHash | Use + addressing for routing — it's the primary threading mechanism |
| Using TextBody instead of StrippedTextReply | StrippedTextReply removes quoted content from replies |
| No size limit on body parser | Set body parser limit to 50mb for messages with attachments |
| Slow webhook processing | Process async (queue the work) and respond 200 immediately |
| Ignoring ContentID on attachments | Attachments with ContentID are inline images, not standalone files |
StrippedTextReply strips quoted content, giving you just the new reply textMailboxHash field is the portion after + in the recipient address — use it for routingtools
Use when setting up Postmark webhooks for tracking email delivery, bounces, opens, clicks, spam complaints, or subscription changes — includes webhook configuration, payload handling, and security.
data-ai
Use when creating, managing, or sending with Postmark server-side email templates — Handlebars syntax, layout inheritance, template validation, and cross-server pushing.
data-ai
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.
testing
Use when asking about email deliverability, compliance (CAN-SPAM, GDPR, CASL), transactional email design patterns, list management, testing safely, or general email best practices — provider-agnostic knowledge with Postmark-specific guidance.