Webhook
訂閱您自己的系統 Flash 事件。為您關注的事件註冊一個 HTTPS 端點,並且 Flash 每枚導彈發射瞬間都會送出一個帶有簽名的薄型有效載荷—— 不進行輪詢,不進行夜間匯出簽名在後面 標準 Webhooks 規格、交付重試(採用退避策略),並且每次嘗試都會被記錄下來。
註冊端點
建立一個包含 webhooks:manage 範圍的 Bearer fl_live_ 金鑰的 webhook 端點。透過 HTTPS 傳送 url 請求,並附上您希望傳遞的 eventTypes 清單。 只會在回應中傳回一次,之後不再回傳-請妥善保管;您需要它來驗證每次傳遞。
curl -X POST https://flash.socialhub.ai/api/v2/webhooks \
-H "Authorization: Bearer fl_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.your-app.com/flash",
"description": "prod ingest",
"eventTypes": ["member.created", "loyalty.tier_upgraded"]
}'
# 201 Created
# {
# "data": {
# "id": "whe_...",
# "url": "https://hooks.your-app.com/flash",
# "eventTypes": ["member.created", "loyalty.tier_upgraded"],
# "status": "active",
# "secret": "whsec_..." <-- shown ONCE, store it now
# }
# }稍後可使用 GET /api/v2/webhooks(列表,無密鑰)、PATCH /api/v2/webhooks/{id}(更改 URL/事件/狀態,或發送 { "action": "rotate_secret" } 以滾動密鑰)和 DELETE /api/v2/webhooks/{id} 管理端點。您也可以透過 設定 → Webhooks 註冊、測試 ping 和重播發送。
活動目錄
詢問 API 您可以訂閱哪些事件。目錄源自即時自動化觸發的真實資料來源,因此僅列出今天實際觸發的事件,並按生命週期階段(獲客、互動、忠誠度、留存、商務、擁護)分組,每個事件都包含其事件鍵、描述和範例有效負載。回應還記錄了共享有效負載信封和簽名標頭。
curl https://flash.socialhub.ai/api/v2/events/catalog \
-H "Authorization: Bearer fl_live_..."
# { "data": { "events": [
# { "event": "member.created", "label": "Member created",
# "stage": "acquisition", "examplePayload": { ... } },
# { "event": "loyalty.tier_upgraded", "label": "Tier upgraded",
# "stage": "loyalty", "examplePayload": { ... } },
# ...
# ],
# "payload": { "schema": { ... }, "headers": { ... } }
# } }訂閱時,將事件的 event 鍵直接放入 eventTypes array 中。需要 webhooks:manage 權限範圍。
輕量級、非個人識別資訊 (PII) 有效載荷
每次投遞都包含相同的小信封:一個穩定的 id、事件 type、一個 created_at timestamp 和一個緊湊的 data 資料塊。酬載 絕不包含個人資料-不包含電子郵件、姓名、電話號碼或地址。當您需要成員的詳細資訊時,請使用 member_ref 通過已認證的 API 來取得這些資訊。這樣可以將個人識別資訊 (PII) 保留在成員記錄中,而不會記錄在您的 Webhook 日誌中。
POST https://hooks.your-app.com/flash
webhook-id: evt_a1b2c3... # stable across retries — dedupe on this
webhook-timestamp: 1767225600 # unix seconds
webhook-signature: v1,K5x0mF...== # base64 HMAC-SHA256 (may be space-separated during rotation)
Content-Type: application/json
{
"id": "evt_a1b2c3...",
"type": "loyalty.tier_upgraded",
"created_at": "2026-01-01T00:00:00.000Z",
"data": {
"resource_id": "mbr_...",
"member_ref": "mbr_...", // opaque member id — fetch PII via the API
"tier": "Gold", // present on loyalty / tier events
"amount_band": "100-500" // coarse band on commerce events — never the exact amount
}
}驗證每個簽名
Flash 使用 標準 Webhook (HMAC-SHA256),透過端點的 whsec_ 金鑰對每個交付進行簽署。重新計算 {webhook-id}.{webhook-timestamp}.{body} 的簽名,並在恆定時間內與 webhook-signature 標頭進行比較。拒絕任何不匹配的交付,並拒絕過舊而無法防禦重播攻擊的 webhook-timestamp 密鑰。根據規範,簽章金鑰是 whsec_ 前綴之後的金鑰的 base64 解碼位元組。在金鑰輪換期間,標頭包含多個以空格分隔的 v1,… 值——如果 中有任何 匹配,則接受交付。
import crypto from "node:crypto";
// req.body must be the RAW, unparsed request body string.
function verifyFlashWebhook(headers, rawBody, whsec) {
const id = headers["webhook-id"];
const ts = headers["webhook-timestamp"];
const sigHeader = headers["webhook-signature"]; // "v1,<b64> v1,<b64> ..."
// reject stale deliveries (5 min tolerance)
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(whsec.replace(/^whsec_/, ""), "base64");
const expected = crypto
.createHmac("sha256", key)
.update(`${id}.${ts}.${rawBody}`)
.digest("base64");
return sigHeader
.split(" ")
.map((p) => p.split(",")[1])
.some((sig) =>
sig &&
sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)),
);
}重試、冪等性和交付日誌
快速回款 2xx
使用任何 2xx 進行確認,並非同步執行您的工作。 429、5xx、逾時或連線錯誤均視為可重試。
自動重試退避
可重試的失敗會採用指數級全抖動退避策略進行重試。 3xx 或非 429 的 4xx 會被視為永久性失敗,不會重試。
透過 webhook-id 進行去重
id 在重試中始終相同,因此至少一次交付是安全的:將重複的 webhook-id 視為已處理。
發送日誌和回放
每次嘗試都會被記錄下來。如果重試次數用盡,則該次投遞將被標記為失敗;您可以在「設定」→「Webhooks」中查看和重播投遞記錄。