Skip to content

Webhook 回调

创建任务时传入 webhook_url,任务结束后我们会向该地址发送 POST 请求。

请求内容

请求头:

http
Content-Type: application/json
X-Webhook-Signature: 5f2b8c...

请求体:

json
{
  "timestamp": 1789632021,
  "task": {
    "id": "task_3kP9xQ2mZ7aB1cD4eF6gH8jK",
    "idempotency_key": "order_10086",
    "status": "succeeded",
    "output": { "images": ["https://cdn.example.com/outputs/abc.png"] },
    "error": null
  }
}
字段说明
timestamp发送时间(Unix 秒),每次发送都会更新
task任务对象,与查询任务的返回相同。通过 idempotency_key 对应你的业务记录,statussucceededfailed

响应与重试

  • 返回 2xx 即表示接收成功,请在 10 秒内响应。
  • 否则会在 1 分钟、5 分钟、30 分钟、2 小时、6 小时后重试,共发送 6 次。
  • 同一个任务可能收到多次推送,请用 task.id 去重。

校验签名

签名密钥在控制台「Webhook」页面获取。

  1. 计算 HMAC-SHA256(密钥, 原始请求体),与请求头 X-Webhook-Signature 比较,一致才处理。
  2. 检查 timestamp 与当前时间相差不超过 5 分钟。

注意:必须使用收到的原始请求体计算,不要先解析 JSON 再序列化。

js
import crypto from 'node:crypto'

function verify(rawBody, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  if (expected.length !== signature?.length
    || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return false
  }
  const { timestamp } = JSON.parse(rawBody)
  return Math.abs(Date.now() / 1000 - timestamp) < 300
}
python
import hashlib
import hmac
import json
import time

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature or ""):
        return False
    return abs(time.time() - json.loads(raw_body)["timestamp"]) < 300