开发者
为耗时工作提供可靠信号
当任务、域名、发布或批准发生变化时,发送带签名的 HTTPS 请求,并保证至少投递一次。
注册端点
在开发者页面添加端点,或使用 webhooks:write 权限范围:
curl -s -X POST "$BASE/workspaces/$WS/webhooks" \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: $(openssl rand -hex 16)" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/mini-casa","events":["job.updated","publication.updated"]}'响应包含 signing_secret(whsec_...),它只会显示一次。URL 必须通过 443 端口使用 HTTPS,且只能解析到公共地址;私有地址、回环地址和内部目标会被拒绝。每次投递前都会重新检查,且绝不会跟随重定向。一个工作区最多可有 10 个启用中的端点。
证明你拥有控制权
新端点最初处于 pending_verification 状态。验证按钮(或 POST /webhooks/{id}/verify)会发送一次带签名的 webhook.challenge。返回 2xx 状态码和 {"challenge": "<data.challenge>"},端点就会变为 active。
事件
job.updated:任务或批量运行的状态发生变化。job.item.updated:批量运行中某个项目的状态发生变化。domain.updated:自定义域名进入不同检查阶段。publication.updated:构建开始、成功、失败或被后续构建取代。approval.updated:批准请求得到决定、到期或被使用。billing.entitlement.updated:套餐或权益发生变化。
请求体只携带 ID 和状态,绝不包含凭据、内容或个人数据。请通过 API 获取当前状态。
验证每一个请求
每个请求都包含 SmallWorld-Webhook-Id、SmallWorld-Webhook-Timestamp(Unix 秒数)和 SmallWorld-Webhook-Signature(v1=<hex>,kid=<key id>)。签名是以你的密钥对 <timestamp>.<raw body> 计算的 HMAC-SHA256。请在进行任何 JSON 解析之前使用原始请求体,以恒定时间比较签名,并拒绝时间戳与你的时钟相差超过五分钟的请求。
import crypto from "node:crypto";
export function verify(secret, timestamp, rawBody, header) {
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
const given = /v1=([a-f0-9]+)/.exec(header)?.[1] ?? "";
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && given.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}投递保证
- 至少一次。请求会持续重试,直到你返回
2xx。每次重试和重新投递时,事件 ID 都保持不变,因此请保存它,并忽略已经处理过的 ID。 - 重试间隔会从一分钟逐步延长到一天,总计持续 72 小时。此后,投递会成为死信;你可以在开发者页面或使用
redeliver_webhook再次发送。 - 一个端点连续有 10 个事件成为死信后,会被暂停,直到你恢复它。
- 不保证投递顺序:请将状态视为提示,并读取资源以确认。