Webhook
1tokens 會在平台內發生事件時,向你在控制台登記的 HTTPS 位址發送帶簽名的 POST 請求。在控制台「Webhook」頁新建端點、勾選要訂閱的事件即可,新端點最遲 30 秒內生效。
事件目錄
模型
| 類型 | 名稱 | data 欄位 |
|---|---|---|
model.listed | 模型上架 | model tenant_id previous_status status actor |
model.delisted | 模型下架 | model tenant_id previous_status status actor |
model.deprecated | 模型棄用 | model previous_status status actor |
model.price.changed | 售價變動 | model tenant_id pricing_status sale_price_mode multiplier price changes actor |
model.user_price.changed | 專屬價變動 | model user_id multiplier changes actor |
model.updated | 模型更新 | model changes actor |
API Key
| 類型 | 名稱 | data 欄位 |
|---|---|---|
api_key.created | Key 建立 | api_key actor |
api_key.updated | Key 修改 | api_key changes actor |
api_key.revoked | Key 吊銷 | api_key previous_status status actor |
api_key.expired | Key 過期 | api_key actor |
api_key.quota.exhausted | 額度用盡 | api_key quota_limit_usd consumed_usd actor |
api_key.rate_window.exceeded | 時間窗超限 | api_key window limit_usd consumed_usd actor |
帳戶
| 類型 | 名稱 | data 欄位 |
|---|---|---|
account.balance.low | 餘額不足 | account threshold_usd request_id actor |
account.balance.exhausted | 餘額耗盡 | account reason request_id actor |
account.credit.topup | 到帳 | account amount_usd amount_credits source order_no actor |
account.credit.adjusted | 調額 | account delta_usd delta_credits kind reason actor |
account.trial.granted | 試用發放 | account amount_credits actor |
account.disabled | 帳號停用 | account previous_status status actor |
account.enabled | 帳號恢復 | account previous_status status actor |
account.password.reset | 密碼重設 | account actor |
訂單
| 類型 | 名稱 | data 欄位 |
|---|---|---|
order.created | 訂單建立 | order actor |
order.paid | 付款成功 | order paid_at previous_status status actor |
order.failed | 付款失敗 | order reason previous_status status actor |
order.redeemed | 兌換成功 | order code_masked actor |
訂閱
| 類型 | 名稱 | data 欄位 |
|---|---|---|
subscription.started | 訂閱開通 | subscription previous_status status actor |
subscription.renewed | 訂閱續費 | subscription actor |
subscription.upgraded | 訂閱升級 | subscription changes actor |
subscription.cancelled | 訂閱取消 | subscription previous_status status actor |
subscription.payment_failed | 續費失敗 | subscription reason actor |
subscription.quota.exhausted | 配額用盡 | subscription request_id actor |
任務
| 類型 | 名稱 | data 欄位 |
|---|---|---|
task.video.succeeded | 影片完成 | task video_count actor |
task.video.failed | 影片失敗 | task actor |
task.generation.succeeded | 生成完成 | task media output_count actor |
task.generation.failed | 生成失敗 | task media actor |
公告
| 類型 | 名稱 | data 欄位 |
|---|---|---|
content.changelog.published | 更新日誌發布 | entry actor |
content.article.published | 文章發布 | article tenant_id previous_status status actor |
content.article.unpublished | 文章下架 | article tenant_id previous_status status actor |
Webhook
| 類型 | 名稱 | data 欄位 |
|---|---|---|
webhook.endpoint.auto_disabled | 端點被停用 | endpoint reason consecutive_failures actor |
推送格式
所有事件共用同一個信封:id 事件唯一 id、type 事件類型、domain 功能域、created_at 發生時間(ISO 8601 UTC)、api_version 版本、data 事件內容。data 裡鍵名一律 snake_case,時間以 _at 結尾,金額是十進位字串(_usd / _credits),沒有值的欄位明確為 null。同一事件可能因重試重複送達,請按 id 做冪等。
{ "id": "evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8B", "type": "model.price.changed", "domain": "model", "created_at": "2026-11-04T08:12:33Z", "api_version": "2026-11-04", "data": { }}請求標頭
POST https://your-server.example/webhooksContent-Type: application/jsonUser-Agent: Webhooks/1.0X-Event-Id: evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8BX-Event-Type: model.price.changedX-Delivery-Id: 123456X-Timestamp: 1762243953X-Signature: v1=5f1c…驗證簽名
用建立端點時保存的密鑰,對「X-Timestamp 的值 + 英文句點 + 原始請求體位元組」計算 HMAC-SHA256,與 X-Signature 裡 v1= 後面的十六進位做常數時間比較;時間戳與當前時間相差超過 5 分鐘應拒收。先回 2xx 再處理業務,整個回應需在 10 秒內完成。
import { createHmac, timingSafeEqual } from "node:crypto";export function verify(secret, headers, rawBody) { const ts = headers["x-timestamp"]; if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const expected = createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex"); const given = String(headers["x-signature"]).replace(/^v1=/, ""); return given.length === expected.length && timingSafeEqual(Buffer.from(given, "hex"), Buffer.from(expected, "hex"));}import hmac, hashlib, timedef verify(secret: str, headers: dict, raw_body: bytes) -> bool: ts = headers["X-Timestamp"] if abs(time.time() - int(ts)) > 300: return False expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest() given = headers["X-Signature"].removeprefix("v1=") return hmac.compare_digest(given, expected)func verify(secret string, ts, sig string, body []byte) bool { t, _ := strconv.ParseInt(ts, 10, 64) if math.Abs(float64(time.Now().Unix()-t)) > 300 { return false } mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(ts + ".")) mac.Write(body) expected := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(strings.TrimPrefix(sig, "v1=")), []byte(expected))}重試與自動停用
非 2xx 回應或 10 秒內未回應都算失敗,最多嘗試 7 次,間隔如下表。全部失敗後該條投遞標記為耗盡;同一端點連續 10 條投遞耗盡會被自動停用並郵件通知,修復後可在控制台重新啟用。任一條投遞都可在控制台手動重發。
| 第 N 次嘗試 | 距上一次 |
|---|---|
| 1 | 0 |
| 2 | 1 min |
| 3 | 5 min |
| 4 | 30 min |
| 5 | 2 h |
| 6 | 6 h |
| 7 | 24 h |
接收位址要求
必須是公網可達的 HTTPS 位址;解析到內網、迴環、鏈路本地等位址的網域會被拒絕。不跟隨重新導向,回應內容最多讀取 64 KB。

