Webhook
EasyPM 在关键事件发生时通过 HTTP POST 推送到你的端点。HMAC-SHA256 签名 + 1m/5m/30m/2h/12h 重试。
Endpoints
注册 Webhook
POST /openapi/v1/apps/:clientId/webhooks
Body:
{
"url": "https://your-endpoint.example.com/easypm",
"events": [
"note.created",
"task.status_changed",
"risk.opened",
"risk.resolved"
]
}
成功响应(仅此一次包含 Secret):
{
"success": true,
"data": {
"id": "wh_abc123",
"url": "https://...",
"events": ["..."],
"secret": "whsec_BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"status": "ACTIVE",
"createdAt": "2026-08-07T15:00:00.000Z"
}
}
列出 Webhook
GET /openapi/v1/apps/:clientId/webhooks
返回的列表不包含 Secret(它只显示一次)。
测试 Webhook
POST /openapi/v1/apps/:clientId/webhooks/:subId/test
触发一次 webhook.test ping 事件。响应:
{
"success": true,
"data": {
"delivered": true,
"status": 200
}
}
停用
DELETE /openapi/v1/apps/:clientId/webhooks/:subId
立即停止投递,但保留订阅行(不物理删除)。
事件类型
完整列表见 /openapi/v1/openapi.json。常用:
| 事件 | 触发时机 |
|---|---|
note.created | 创建笔记 |
note.updated | 编辑笔记 |
note.deleted | 删除笔记 |
action-item.created | 创建行动项 |
action-item.updated | 编辑 |
action-item.deleted | 删除 |
task.created | 创建任务 |
task.status_changed | 任务状态变化 |
task.assigned | 重新指派 |
task.deleted | 删除 |
risk.created | 登记风险 |
risk.opened | OPEN |
risk.resolved | RESOLVED |
risk.deleted | 删除 |
project.created | 新建项目 |
project.member_added | 添加成员 |
project.member_removed | 移除成员 |
webhook.test | 你手动点「测试」 |
投递细节
每次 EasyPM 推送:
POST <your-url>
Content-Type: application/json
User-Agent: EasyPM-Webhooks/1.0
X-EasyPM-Event: task.status_changed
X-EasyPM-Delivery-Id: evt_abc123
X-EasyPM-Signature: t=1765432100,v1=7c0b2a1c...
{
"id": "evt_abc123",
"type": "task.status_changed",
"data": {
"taskId": "...",
"from": "TODO",
"to": "IN_PROGRESS"
},
"sentAt": "2026-08-07T15:00:00.000Z"
}
签名验证
v1 = HMAC-SHA256( secret, `${t}.${raw_body}` )
校验步骤:
- 读
t和v1从X-EasyPM-Signature: t=...,v1=... |now - t| < 300s否则拒绝(防 replay)- 在你的服务上:
v1 == HMAC(secret, t + "." + raw_body) - 用
crypto.timingSafeEqual比较
重试
每次尝试:
- 立即(首次)
- 失败 → 1m 后
- 仍失败 → 5m 后
- 仍失败 → 30m 后
- 仍失败 → 2h 后
- 仍失败 → 12h 后
- 仍失败 → 标记
DEAD,不再重试
任何 2xx 响应(200 / 201 / 202 / 204)视为成功。
安全
- Webhook URL 必须是 HTTPS(HTTP URL 在创建时被 400 拒绝)
- 内网 IP 段(10.x / 192.168.x / 127.x)被 SSRF guard 拒绝
- Secret 仅创建时返回一次