listmonk 如何接入 SendGrid / Twilio 签名事件 Webhook 记录弹跳
【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk
如果 listmonk 的发送走 SendGrid 或 Twilio Email 的 SMTP 通道,而你又想把发送产生的弹跳自动记回 listmonk(而不是靠 POP3 邮箱扫描),可以使用这两个服务商提供的签名事件 Webhook。listmonk 内置了接收端点/webhooks/service/sendgrid,在 listmonk 侧启用弹跳处理、填入签名密钥,并在服务商侧把 Webhook URL 指过去之后,listmonk 会对每次回调验签、解析弹跳事件并写入 bounces 表,再按你配置的弹跳处置策略(退订、拉黑、删除等)执行计数。
以下内容基于 docs/docs/content/bounces.md、frontend/src/views/settings/bounces.vue 和 internal/bounce/webhooks/sendgrid.go。前提是你的 listmonk 实例可被公网访问——文档统一以https://listmonk.yoursite.com/...举例,其中listmonk.yoursite.com是占位域名,需要换成你的实际域名。
一、启用弹跳处理与 Webhook
文档明确说明:弹跳处理(bounce processing)未启用时,POP3 扫描和 Webhook API 都不可用,所以第一步必须打开它。
在管理面板Settings → Bounces页面(对应 frontend/src/views/settings/bounces.vue 中的字段)依次设置:
- 打开Enable bounce processing(配置项
bounce.enabled); - 为三种弹跳类型配置处置策略:每一行分别对应 soft / hard / complaint,设置Bounce count(每个订阅者的弹跳计数)与Action,可选
None、Unsubscribe、Blocklist、Delete; - 打开Enable bounce webhooks(配置项
bounce.webhooks_enabled)。
count/action 的组合可参考文档在 Amazon SES 一节给出的示例值:Soft2/None,Hard1/Blocklist,Complaint1/Blocklist。
二、配置签名密钥与 Webhook URL
listmonk 侧
仍在Settings → Bounces中,打开Enable bounce webhooks后会出现各服务商的配置区。在 SendGrid 一行打开Enable SendGrid(配置项bounce.sendgrid_enabled),并在SendGrid Key(配置项bounce.sendgrid_key)中粘贴签名密钥。该密钥来自 SendGrid 事件 Webhook 的安全特性(event webhook security features),文档外部链接指向的就是 SendGrid 官方对应章节,用于生成签名用的公钥。
listmonk 会把这个密钥做 base64 解码、解析为 ECDSA 公钥,并用它验证每次回调(见 internal/bounce/webhooks/sendgrid.go):
- 签名来自请求头
X-Twilio-Email-Event-Webhook-Signature(base64 编码的 ASN.1 R/S 结构); - 时间戳来自请求头
X-Twilio-Email-Event-Webhook-Timestamp; - 校验方式:对「时间戳 + 请求体」取 SHA-256 后做 ECDSA 验签。
SendGrid 与 Twilio Email 使用同一套签名方案,因此两者共用这一个端点和同一个密钥字段。
服务商侧
在 SendGrid / Twilio Email 的事件 Webhook 设置中,把回调 URL 登记为(文档原文示例):
https://listmonk.yoursite.com/webhooks/service/sendgridURL 中的listmonk.yoursite.com是文档占位符,替换成你的 listmonk 公网域名;同时按服务商要求开启签名发送。控制台中的具体操作路径以 SendGrid / Twilio 官方事件 Webhook 文档为准(文档表格的 "More info" 列给出了出处)。
三、回调进来后 listmonk 做了什么
入口是 cmd/bounce.go 的BounceWebhook,路由参数service为sendgrid且密钥已配置时进入该分支,收到一个请求后:
- 验签:先读取上面两个请求头做签名校验;密钥错误、签名无效时请求直接返回
400,事件不会入库。 - 只处理弹跳事件:请求体是事件 JSON 数组,
event不等于"bounce"的条目(delivered、open、click 等)被直接跳过。 - 软硬弹跳分类:按事件的
bounce_classification字段,technical和content记为soft,其余情况记为hard。 - 写入 bounces 表:
source固定记为sendgrid,email转小写,原始请求全文保存在meta中备查。 - 关联活动:事件 JSON 中的
XListmonkCampaign字段(SendGrid 会把邮件里的 X- 头展平进事件,对应 listmonk 发出的X-Listmonk-Campaign邮件头)被用作campaign_uuid,使弹跳能对应到具体活动。
所有弹跳随后统一走Record落库,并按你在第一节配置的 Bounce count / Action 策略对订阅者执行处置。
四、验证接入是否生效
弹跳真实发生(例如向一个不存在的收件人地址发信触发弹跳)后,用文档 "Exporting bounces" 一节给出的方式核对:
JSON API 查询(username/password为你的 listmonk 凭据,示例来自文档,请替换实际值):
curl -u 'username:password' 'http://localhost:9000/api/bounces'或直接查数据库:
SELECT bounces.created_at, bounces.subscriber_id, subscribers.uuid AS subscriber_uuid, subscribers.email AS email FROM bounces LEFT JOIN subscribers ON (subscribers.id = bounces.subscriber_id) ORDER BY bounces.created_at DESC LIMIT 1000;通过该 Webhook 写入的记录source为sendgrid,meta保留原始事件内容,据此可以和其他渠道(POP3 扫描、其他服务商)的记录区分。管理面板的 Bounces 页面同样可以看到这些记录。
五、已知限制
- 弹跳处理未启用时,
/webhooks/bounce与各服务商 Webhook 端点都不可用,文档明确它们只在开启后生效。 - 只产生 hard / soft:SendGrid/Twilio 通道不会生成
complaint类型记录。 - 非弹跳事件不落库:一次回调里若只有投递状态或打开事件,不会产生 bounces 记录。
- 密钥必须与服务商端一致:签名校验不通过时所有请求都以 400 拒绝,此时先核对 SendGrid Key 填的是否为当前有效的签名密钥。
可选分支:用自定义脚本记录弹跳
如果你的弹跳来自自己的邮箱、数据库或邮件服务器日志,而不是签名 Webhook,可以调用通用POST /webhooks/bounce接口(见 docs/docs/content/bounces.md)。必填字段:email与subscriber_uuid至少一个,外加source和type(hard/soft),可选campaign_uuid与任意metaJSON:
curl -u 'api_username:access_token' -X POST 'http://localhost:9000/webhooks/bounce' \ -H "Content-Type: application/json" \ --data '{"email": "user1@mail.com", "campaign_uuid": "9f86b50d-5711-41c8-ab03-bc91c43d711b", "source": "api", "type": "hard", "meta": "{\"additional\": \"info\"}}'其中api_username/access_token为你的 API 凭据,user1@mail.com与 UUID 是文档示例,实际使用时替换为真实值。注意文档说明该接口的type字段"当前不影响弹跳的处置方式"——实际处置始终由 Bounces 页面的 count/action 配置决定。
【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考