Webhook
背景非同步作業完成時,Webhook 會即時傳送 HTTP POST 通知,例如貼文發佈成功、發佈失敗或回覆已送出。
| 方法 | 路徑 | 說明 | 必要權限範圍 |
|---|---|---|---|
POST | /v1/brand/webhooks | 註冊新的 Webhook 端點 | webhooks:write 或 * |
GET | /v1/brand/webhooks | 列出已註冊的 Webhook 訂閱 | webhooks:read 或 * |
DELETE | /v1/brand/webhooks/{webhook_id} | 刪除 Webhook 訂閱 | webhooks:write 或 * |
🔔 支援的事件類型
Section titled “🔔 支援的事件類型”| 事件名稱 | 觸發條件 |
|---|---|
sns.publish.succeeded | 貼文已成功發佈至目標社群網路 |
sns.publish.failed | 貼文發佈失敗 (包含錯誤碼與是否可重試) |
sns.publish.blocked_entitlement | 因配額限制或方案過期而阻擋發佈 |
sns.reply.succeeded | 留言回覆已成功發佈 |
sns.reply.failed | 回覆留言發送失敗 |
sns.provider.notification_received | 收到來自外部 SNS 提供者的即時 Webhook |
📦 Webhook 酬載範例 (sns.publish.succeeded)
Section titled “📦 Webhook 酬載範例 (sns.publish.succeeded)”{ "event_id": "evt_01jm8za2example", "event_type": "sns.publish.succeeded", "schema_version": 1, "occurred_at": "2026-08-16T03:30:00Z", "brand_ref": "brand_01jm8v4k9example", "trace_id": "trc_9a8b7c6d", "data": { "content_id": "content_01jm8za2example", "publish_job_id": "job_01jm8za2jobexample", "sns_type": "instagram", "connection_id": "conn_01jm8x9k2example", "provider_post_id": "18029384756102938", "status": "succeeded" }}🔐 簽名驗證 (HMAC SHA256)
Section titled “🔐 簽名驗證 (HMAC SHA256)”Webhook 請求包含自定義的驗證標頭:
Social-Webhook-Signature: 簽名酬載 (v1=hex_signature)Social-Webhook-Timestamp: 以秒為單位的 Unix 時間戳記Social-Webhook-Id: 傳送識別碼
Node.js (Crypto) 驗證
Section titled “Node.js (Crypto) 驗證”import crypto from 'node:crypto';
export function verifyWebhookSignature(rawBody: string, signatureHeader: string, timestampHeader: string, secret: string): boolean { const signedPayload = `${timestampHeader}.${rawBody}`; const expectedSignature = crypto.createHmac('sha256', secret).update(signedPayload).digest('hex');
const actual = Buffer.from(signatureHeader); const expected = Buffer.from(`v1=${expectedSignature}`); return actual.length === expected.length && crypto.timingSafeEqual(actual, expected);}Python (hmac) 驗證
Section titled “Python (hmac) 驗證”import hmacimport hashlib
def verify_webhook_signature(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool: signed_payload = f"{timestamp}.".encode('utf-8') + raw_body expected = hmac.new(secret.encode('utf-8'), signed_payload, hashlib.sha256).hexdigest() return hmac.compare_digest(signature, f"v1={expected}")[!CAUTION] 簽名驗證必須使用未經解析的原始請求主體 (raw request body)。