Loading...
Loading...
Receive and process Postmark webhooks. Use when setting up Postmark webhook handlers, handling email delivery events, processing bounces, opens, clicks, spam complaints, or subscription changes.
npx skill4agent add hookdeck/webhook-skills postmark-webhooks// Express - Basic Auth in URL
// Configure webhook URL in Postmark as:
// https://username:password@yourdomain.com/webhooks/postmark
app.post('/webhooks/postmark', express.json(), (req, res) => {
// Basic auth is handled by your web server or proxy
// Additional validation can check expected payload structure
const event = req.body;
// Validate expected fields exist
if (!event.RecordType || !event.MessageID) {
return res.status(400).send('Invalid payload structure');
}
// Process event
console.log(`Received ${event.RecordType} event for ${event.Email}`);
res.sendStatus(200);
});
// Alternative: Token in URL
// Configure webhook URL as:
// https://yourdomain.com/webhooks/postmark?token=your-secret-token
app.post('/webhooks/postmark', express.json(), (req, res) => {
const token = req.query.token;
if (token !== process.env.POSTMARK_WEBHOOK_TOKEN) {
return res.status(401).send('Unauthorized');
}
const event = req.body;
console.log(`Received ${event.RecordType} event`);
res.sendStatus(200);
});// Postmark sends one event per request (not batched)
app.post('/webhooks/postmark', express.json(), (req, res) => {
const event = req.body;
switch (event.RecordType) {
case 'Bounce':
console.log(`Bounce: ${event.Email} - ${event.Type} - ${event.Description}`);
// Update contact as undeliverable
break;
case 'SpamComplaint':
console.log(`Spam complaint: ${event.Email}`);
// Remove from mailing list
break;
case 'Open':
console.log(`Email opened: ${event.Email} at ${event.ReceivedAt}`);
// Track engagement
break;
case 'Click':
console.log(`Link clicked: ${event.Email} - ${event.OriginalLink}`);
// Track click-through rate
break;
case 'Delivery':
console.log(`Delivered: ${event.Email} at ${event.DeliveredAt}`);
// Confirm delivery
break;
case 'SubscriptionChange':
console.log(`Subscription change: ${event.Email} - ${event.ChangedAt}`);
// Update subscription preferences
break;
case 'Inbound':
console.log(`Inbound email from: ${event.Email} - Subject: ${event.Subject}`);
// Process incoming email
break;
case 'SMTP API Error':
console.log(`SMTP API error: ${event.Email} - ${event.Error}`);
// Handle API error, maybe retry
break;
default:
console.log(`Unknown event type: ${event.RecordType}`);
}
res.sendStatus(200);
});| Event | RecordType | Description | Key Fields |
|---|---|---|---|
| Bounce | | Hard/soft bounce or blocked email | Email, Type, TypeCode, Description |
| Spam Complaint | | Recipient marked as spam | Email, BouncedAt |
| Open | | Email opened (requires open tracking) | Email, ReceivedAt, Platform, UserAgent |
| Click | | Link clicked (requires click tracking) | Email, ClickedAt, OriginalLink |
| Delivery | | Successfully delivered | Email, DeliveredAt, Details |
| Subscription Change | | Unsubscribe/resubscribe | Email, ChangedAt, SuppressionReason |
| Inbound | | Incoming email received | Email, FromFull, Subject, TextBody, HtmlBody |
| SMTP API Error | | SMTP API call failed | Email, Error, ErrorCode, MessageID |
# For token-based authentication
POSTMARK_WEBHOOK_TOKEN="your-secret-token-here"
# For basic auth (if not using URL-embedded credentials)
WEBHOOK_USERNAME="your-username"
WEBHOOK_PASSWORD="your-password"brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/postmark