指南

Webhooks

接收批量任务完成通知

对于运行时间超过一分钟的批量任务,Webhook 比轮询更便宜也更快。当任务到达终态时,Thunderbit 会向你的 URL 发起 POST。

提交时配置

{
  "urls": ["https://example.com/page1"],
  "webhook": {
    "url": "https://your-server.com/api/webhook/distill",
    "secret": "whsec_your_secret_key",
    "headers": { "X-Custom-Auth": "your-token" }
  }
}

webhook.headers 字段接受自定义请求头映射,但该字段为预留字段,服务端目前尚未发送。请仅按照下方文档列出的请求头进行验签。

请求头

每次投递都会包含以下 4 个请求头:

HeaderValue
Content-Typeapplication/json
X-Webhook-Eventbatch.completed
X-Webhook-TimestampUnix 时间戳(毫秒
X-Webhook-Signaturesha256=<base64-encoded HMAC-SHA256>

载荷

{
  "id": "batch_a1b2c3d4...",
  "jobType": "batch_distill",
  "status": "COMPLETED",
  "total": 50,
  "completed": 49,
  "failed": 1,
  "creditsUsed": 100,
  "createdAt": "2026-04-26T10:00:00Z",
  "completedAt": "2026-04-26T10:05:23Z"
}
  • jobType 取值为 batch_distillbatch_extract
  • status 取值为 COMPLETEDFAILEDCANCELLED

载荷有意保持精简——收到回调后请通过 GET /batch/distill/{id}(或 /batch/extract/{id})拉取完整结果。

签名校验

设置了 secret 后,每次投递都会带上 X-Webhook-Signature 请求头。

  • 算法:HMAC-SHA256
  • 待签名字符串:<X-Webhook-Timestamp>.<raw-json-body>(两者之间是一个字面意义的 .
  • 输出格式:sha256=<base64-encoded-hash>(Base64,不是 hex)

请使用原始请求字节进行校验(不要重新序列化 JSON),然后做恒定时间比较。

import hmac, hashlib, base64

def verify(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
    base = f"{timestamp}.{raw_body.decode('utf-8')}".encode('utf-8')
    digest = hmac.new(secret.encode('utf-8'), base, hashlib.sha256).digest()
    expected = "sha256=" + base64.b64encode(digest).decode('ascii')
    return hmac.compare_digest(expected, signature)

重放保护

如果 |now - X-Webhook-Timestamp| > 60000(60 秒窗口),请拒绝该请求。时间戳为 毫秒 Unix 时间戳。生产环境绝不要信任未签名的 webhook。

重试行为

  • 单次投递超时:30 秒
  • 最大重试次数:3
  • 退避策略:指数退避,起始 1 秒,上限 10 秒
  • 端到端总窗口:约 120 秒

所有重试都失败后,任务在我们这边仍是已完成状态——你的接收端必须是幂等的(建议以 id 作为去重键)。