postmark-webhooks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePostmark Webhooks
Postmark Webhooks
When to Use This Skill
何时使用该Skill
- Setting up Postmark webhook handlers for email event tracking
- Processing email delivery events (bounce, delivered, open, click)
- Handling spam complaints and subscription changes
- Implementing email engagement analytics
- Troubleshooting webhook authentication issues
- 搭建用于邮件事件追踪的Postmark webhook处理器
- 处理邮件投递事件(退信、投递成功、打开、点击)
- 处理垃圾邮件投诉和订阅变更
- 实现邮件互动分析
- 排查webhook认证问题
Essential Code
核心代码
Authentication
认证
Postmark does NOT use signature verification. Instead, webhooks are authenticated by including credentials in the webhook URL itself.
javascript
// 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不使用签名验证,而是通过在webhook URL中包含凭证来进行认证。
javascript
// Express - URL中的基础认证
// 在Postmark中配置webhook URL为:
// https://username:password@yourdomain.com/webhooks/postmark
app.post('/webhooks/postmark', express.json(), (req, res) => {
// 基础认证由你的web服务器或代理处理
// 可额外验证预期的负载结构
const event = req.body;
// 验证必填字段是否存在
if (!event.RecordType || !event.MessageID) {
return res.status(400).send('Invalid payload structure');
}
// 处理事件
console.log(`Received ${event.RecordType} event for ${event.Email}`);
res.sendStatus(200);
});
// 替代方案:URL中的Token
// 配置webhook URL为:
// 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);
});Handling Multiple Events
处理多类事件
javascript
// 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);
});javascript
// Postmark每次请求发送一个事件(不批量发送)
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}`);
// 将联系人标记为无法投递
break;
case 'SpamComplaint':
console.log(`Spam complaint: ${event.Email}`);
// 从邮件列表中移除
break;
case 'Open':
console.log(`Email opened: ${event.Email} at ${event.ReceivedAt}`);
// 追踪互动情况
break;
case 'Click':
console.log(`Link clicked: ${event.Email} - ${event.OriginalLink}`);
// 追踪点击率
break;
case 'Delivery':
console.log(`Delivered: ${event.Email} at ${event.DeliveredAt}`);
// 确认投递成功
break;
case 'SubscriptionChange':
console.log(`Subscription change: ${event.Email} - ${event.ChangedAt}`);
// 更新订阅偏好
break;
case 'Inbound':
console.log(`Inbound email from: ${event.Email} - Subject: ${event.Subject}`);
// 处理收到的邮件
break;
case 'SMTP API Error':
console.log(`SMTP API error: ${event.Email} - ${event.Error}`);
// 处理API错误,可尝试重试
break;
default:
console.log(`Unknown event type: ${event.RecordType}`);
}
res.sendStatus(200);
});Common Event Types
常见事件类型
| 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 |
| 事件 | RecordType | 描述 | 关键字段 |
|---|---|---|---|
| 退信 | | 硬退信/软退信或邮件被拦截 | Email, Type, TypeCode, Description |
| 垃圾邮件投诉 | | 收件人标记邮件为垃圾邮件 | Email, BouncedAt |
| 邮件打开 | | 邮件被打开(需开启打开追踪) | Email, ReceivedAt, Platform, UserAgent |
| 链接点击 | | 链接被点击(需开启点击追踪) | Email, ClickedAt, OriginalLink |
| 投递成功 | | 邮件成功投递 | Email, DeliveredAt, Details |
| 订阅变更 | | 取消订阅/重新订阅 | Email, ChangedAt, SuppressionReason |
| 收到邮件 | | 收到 incoming 邮件 | Email, FromFull, Subject, TextBody, HtmlBody |
| SMTP API错误 | | SMTP API调用失败 | Email, Error, ErrorCode, MessageID |
Environment Variables
环境变量
bash
undefinedbash
undefinedFor token-based authentication
基于Token的认证
POSTMARK_WEBHOOK_TOKEN="your-secret-token-here"
POSTMARK_WEBHOOK_TOKEN="your-secret-token-here"
For basic auth (if not using URL-embedded credentials)
基础认证(若不使用URL嵌入的凭证)
WEBHOOK_USERNAME="your-username"
WEBHOOK_PASSWORD="your-password"
undefinedWEBHOOK_USERNAME="your-username"
WEBHOOK_PASSWORD="your-password"
undefinedSecurity Best Practices
安全最佳实践
- Always use HTTPS - Never configure webhooks with HTTP URLs
- Use strong credentials - Generate long, random tokens or passwords
- Validate payload structure - Check for expected fields before processing
- Implement IP allowlisting - Postmark publishes their IP ranges
- Consider using a webhook gateway - Like Hookdeck for additional security layers
- 始终使用HTTPS - 绝不要使用HTTP URL配置webhook
- 使用强凭证 - 生成长且随机的Token或密码
- 验证负载结构 - 处理前检查必填字段是否存在
- 实施IP白名单 - Postmark会公布其IP范围
- 考虑使用webhook网关 - 例如Hookdeck,以增加额外的安全层
Local Development
本地开发
For local webhook testing, use Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/postmarkNo account required. Provides local tunnel + web UI for inspecting requests.
如需本地测试webhook,可使用Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/postmark无需注册账号,提供本地隧道和用于查看请求的Web UI。
Resources
参考资源
- overview.md - What Postmark webhooks are, common event types
- setup.md - Configure webhooks in Postmark dashboard
- verification.md - Authentication methods and security best practices
- examples/ - Complete implementations for Express, Next.js, and FastAPI
- overview.md - Postmark webhook是什么,常见事件类型
- setup.md - 在Postmark控制台配置webhook
- verification.md - 认证方法和安全最佳实践
- examples/ - Express、Next.js和FastAPI的完整实现示例
Recommended: webhook-handler-patterns
推荐:webhook-handler-patterns
For production-ready webhook handling, also install the webhook-handler-patterns skill:
- Handler sequence - Webhook processing flow
- Idempotency - Prevent duplicate processing
- Error handling - Graceful error recovery
- Retry logic - Handle transient failures
如需生产环境可用的webhook处理方案,还可安装webhook-handler-patterns skill:
- Handler sequence - Webhook处理流程
- Idempotency - 防止重复处理
- Error handling - 优雅的错误恢复
- Retry logic - 处理临时故障
Related Skills
相关Skill
- sendgrid-webhooks - SendGrid webhook handling with ECDSA verification
- resend-webhooks - Resend webhook handling with Svix signatures
- stripe-webhooks - Stripe webhook handling with HMAC-SHA256
- webhook-handler-patterns - Idempotency, error handling, retry logic
- hookdeck-event-gateway - Production webhook infrastructure
- sendgrid-webhooks - 带ECDSA验证的SendGrid webhook处理
- resend-webhooks - 带Svix签名的Resend webhook处理
- stripe-webhooks - 带HMAC-SHA256的Stripe webhook处理
- webhook-handler-patterns - 幂等性、错误处理、重试逻辑
- hookdeck-event-gateway - 生产环境webhook基础设施