Webhook
hook = client.webhooks.create("https://example.com/hook", events=["artifact.updated", "job.completed"])secret = hook["secret"] # この応答でのみ取得可能
# 受信側from d2b.client import _Webhooksok = _Webhooks.verify_delivery(secret, request.headers, raw_body) # 签名 + 发送时间(默认 5 分钟内)- 签名:
X-D2B-Signature-V2: sha256=<hex>= HMAC-SHA256(secret,"{X-D2B-Delivery}.{X-D2B-Timestamp}." + raw_body)。X-D2B-Timestamp是本次尝试的发送时间(UNIX 秒,每次重试都会更新):请拒绝与当前时间相差超过容忍范围(例如 5 分钟)的请求,以防重放。X-D2B-Delivery是投递 ID(重试时保持不变)。请用原始 body校验(不要重新序列化) - 原有的签名
X-D2B-Signature: sha256=<hex>= HMAC-SHA256(secret, raw_body) 仍会发送。它不包含发送时间,因此无法防止重放 - 事件:
artifact.updated/artifact.deleted/artifact.stale/snapshot.committed/source.analyzed/source.materialized/conflict.created/version.branched/version.merged/job.completed - 投递历史:
GET /api/v1/me/webhooks/{id}/deliveries - 重试:非 2xx 响应和连接失败最多重试 14 次,间隔从 30 秒起指数增长到 1 小时(总计约 8 小时)。仍然失败的投递会保留在
deliveries中但不再重试。一个事件只产生一次投递,但响应未送达的尝试会被重试,因此同一投递(相同的X-D2B-Delivery)可能多次到达:请以X-D2B-Delivery为键做幂等处理。payload 中的timestamp是事件发生时间,重试时不会改变
耗时较长的处理(摄取大文件等)推荐用 async=true 转为 job,并等待 job.completed — 比 poll 循环更省,也更扛得住 Agent 的超时。