整合導覽驗證端點約定模型錯誤Webhook
1tokens1tokens
開始使用API 參考

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.createdKey 建立api_key actor
api_key.updatedKey 修改api_key changes actor
api_key.revokedKey 吊銷api_key previous_status status actor
api_key.expiredKey 過期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 次嘗試距上一次
10
21 min
35 min
430 min
52 h
66 h
724 h

接收位址要求

必須是公網可達的 HTTPS 位址;解析到內網、迴環、鏈路本地等位址的網域會被拒絕。不跟隨重新導向,回應內容最多讀取 64 KB。

錯誤

Previous Page

On this page

事件目錄模型API Key帳戶訂單訂閱任務公告Webhook推送格式請求標頭驗證簽名重試與自動停用接收位址要求
{
  "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/webhooks
Content-Type: application/json
User-Agent: Webhooks/1.0
X-Event-Id: evt_01JBXK6Q3N7Y8R2V4W5X6Z7A8B
X-Event-Type: model.price.changed
X-Delivery-Id: 123456
X-Timestamp: 1762243953
X-Signature: v1=5f1c…
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, time

def 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))
}