Webhook
背景非同步作業完成時,Webhook 會即時傳送 HTTP POST 通知,例如貼文發佈成功、發佈失敗或回覆已送出。
📌 端點
섹션 제목: “📌 端點”| 方法 | 路徑 | 說明 | 必要權限範圍 |
|---|---|---|---|
POST | /v1/brand/webhooks/subscriptions | 註冊新的 Webhook 端點 | webhooks:write 或 * |
GET | /v1/brand/webhooks/subscriptions | 列出已註冊的 Webhook 訂閱 | webhooks:read 或 * |
DELETE | /v1/brand/webhooks/subscriptions/{id} | 刪除 Webhook 訂閱 | webhooks:write 或 * |
🔔 支援的事件類型
섹션 제목: “🔔 支援的事件類型”| 事件名稱 | 觸發條件 |
|---|---|
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)
섹션 제목: “📦 Webhook 酬載範例 (sns.publish.succeeded)”{ "event_id": "evt_01jm8za2example", "event_type": "sns.publish.succeeded", "schema_version": 1, "occurred_at": "2026-08-16T03:30:00Z", "producer": "ankk.sns", "brand_ref": "brand_01jm8v4k9example", "idempotency_key": "launch-2026-001", "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", "permalink": "https://www.instagram.com/p/C-example/" }}🔐 簽名驗證 (HMAC SHA256)
섹션 제목: “🔐 簽名驗證 (HMAC SHA256)”Webhook 請求包含自定義的驗證標頭:
Social-Webhook-Signature: 簽名酬載 (t=1723780000,v1=hex_signature)Social-Webhook-Timestamp: 以秒為單位的 Unix 時間戳記Social-Webhook-Id: 事件識別碼
Node.js (Crypto) 驗證
섹션 제목: “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');
return crypto.timingSafeEqual( Buffer.from(signatureHeader), Buffer.from(expectedSignature) );}Python (hmac) 驗證
섹션 제목: “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, expected)[!CAUTION] 簽名驗證必須使用未經解析的原始請求主體 (raw request body)。