Webhooks
Webhook 在事情发生时主动通知你自己的系统,不用反复来查。在控制台添加一个 https 地址并选好事件;每当其中一件事发生,SocialToAI 就向它推送一条签名的 JSON 消息。消息里只有数字、编号和链接,没有任何人写的文字。投递至少一次,失败会在大约一天内重试;Webhook 不收费。
添加端点
- 在控制台打开设置 → Webhook,添加一个端点:你自己的 https 地址,以及它要接收的事件。端点只在控制台管理,没有用 Key 调用的路由。
- 地址必须是 https,并且指向公网。保存前我们会先给它推送一条签名的检查消息(
type是endpoint.check),它必须回 2xx。 - 然后显示签名密钥,只显示这一次。它以
whsec_开头,请当场复制到你的接收端配置里。之后控制台只显示它的最后四位。 - 每个账户最多 10 个端点。端点属于账户,不属于某把 Key:撤销 Key 不影响它。
事件
每条消息都是一个 JSON 对象:id(事件编号)、type、created_at,然后是这个事件自己的字段。summary 里按名字放着这次运行的数字。
| type | 什么时候发 | 字段 |
|---|---|---|
tool_run.finished | 现成工具的一次运行以 ok 或 empty 结束。 | tool, handle, status, summary, cost_credits, quoted_credits, result_url |
tool_run.failed | 现成工具的一次运行以 partial 或 failed 结束。 | tool, handle, status, summary, cost_credits, quoted_credits, result_url |
batch.finished | 一个批量任务结束,不论什么状态。 | tool, batch_id, summary, cost_credits, download_url |
schedule_run.finished | 定期运行的一次运行完成。summary.new_items 是上一次没有的条数,new_links 是其中前几条的链接。 | tool, schedule_id, handle, summary, cost_credits, new_links, report_url |
schedule.alert | 定期运行的主要数字变化超过 alert_percent,或从 0 变成非 0。 | tool, schedule_id, handle, summary, cost_credits, report_url |
balance.low | 余额跌破提醒线,和余额提醒邮件同时发出。 | balance_credits, threshold_credits, billing_url |
daily_cap.reached | 当天(UTC)账户每日上限第一次拒绝调用。 | daily_limit_credits, resets_at, settings_url |
{ "id": "evt_0123456789abcdef01234567", "type": "schedule_run.finished", "created_at": "2026-10-25T09:00:12.000Z", "tool": "search", "schedule_id": "00000000-0000-4000-8000-000000000021", "handle": "res_00000000000040008000000000000003", "summary": { "new_items": 12 }, "cost_credits": 2, "report_url": "https://socialtoai.com/console/tools/"}示例:编号和数字是占位。
校验签名
每条消息带两个请求头。SocialToAI-Signature 是 t=<秒>,v1=<十六进制>:t 是签名时的 Unix 时间,v1 是用你的签名密钥对 <t>.<正文> 做的 HMAC-SHA256(十六进制),正文是原始请求体。SocialToAI-Webhook-Id 是这次投递的编号,重试时不变。
- 用收到的原始正文计算 HMAC,不要先解析 JSON;比较时用常数时间比较。
t和你的时钟相差超过 5 分钟的消息直接拒绝,防止重放。- 同一条消息可能到达不止一次。已经处理过的
id直接跳过;在控制台手动重发的消息id不变,投递编号是新的。
import { createHmac, timingSafeEqual } from "node:crypto";// rawBody: the request body exactly as it arrived, before any JSON parsing.export function verify(rawBody, header, secret) { const parts = Object.fromEntries(header.split(",").map((part) => part.split("="))); const seconds = Number(parts.t); if (!Number.isInteger(seconds) || Math.abs(Date.now() / 1000 - seconds) > 300) return false; const expected = createHmac("sha256", secret).update(`${seconds}.${rawBody}`).digest(); const given = Buffer.from(parts.v1 ?? "", "hex"); return given.length === expected.length && timingSafeEqual(given, expected);}import hashlib, hmac, time# raw_body: the request body as bytes, exactly as it arrived.def verify(raw_body: bytes, header: str, secret: str) -> bool: parts = dict(part.split("=", 1) for part in header.split(",")) seconds = int(parts.get("t", "0")) if abs(time.time() - seconds) > 300: return False signed = f"{seconds}.".encode() + raw_body expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts.get("v1", ""))投递与重试
- 在 8 秒内回 2xx。不跟随重定向;每次推送前都会重新检查地址。
- 其他状态码、没有应答,或 2 分钟内没有结果,都算一次失败的尝试。之后依次在 1 分钟、5 分钟、30 分钟、2 小时、6 小时、24 小时后重试,连首次共 7 次。
- 连续 10 次尝试失败,只暂停这一个端点,并给你发一封邮件;其他照常,定期运行也照常跑。在控制台里可以恢复。
- 控制台保留最近 30 天的投递记录,可以发送测试事件,也可以重发任意一条。
最近更新: