Webhook

取得 API 金鑰

註冊 webhook

註冊一個公開的 https 位址和你想接收的事件。簽章密鑰(whsec_…)只會出現在這次回應中。一個應用程式最多可有 10 個 webhook。

bash
curl -X POST https://api.sofia-post.com/api/v1/webhooks \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://app.example.com/hooks/sofia","events":["post.published","post.failed"]}'

事件

事件data
post.publishedpostId, establishmentId, platform, publishedAt, permalink
post.failedpostId, establishmentId, platform, error
webhook.testwebhookId

Sofia 以 POST 方式傳送每個事件,內容為 JSON { id, type, createdAt, data },並帶有 Sofia-Event-Id、Sofia-Event-Type 和 Sofia-Signature 標頭。只會通知由你的應用程式建立的帖文。

驗證簽章

Sofia-Signature 的格式為 t=<unix time>,v1=<hex>,其中 v1 是用你的密鑰對 <t>.<raw body> 計算的 HMAC-SHA256。請在原始請求本文上驗證,並拒絕過舊的時間戳記:

javascript
import { createHmac, timingSafeEqual } from 'node:crypto';

const isSignedBySofia = (header, rawBody, secret) => {
  const parts = Object.fromEntries(
    (header ?? '').split(',').map((part) => part.trim().split('=', 2)),
  );
  const timestamp = Number(parts.t);
  if (!Number.isInteger(timestamp) || typeof parts.v1 !== 'string') return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest();
  const received = Buffer.from(parts.v1, 'hex');
  return received.length === expected.length && timingSafeEqual(received, expected);
};

傳遞與重試

  • 回傳 2xx 即視為事件已送達。其他回應、重新導向(不會跟隨)或 10 秒內沒有回應,都會在 1 分鐘、5 分鐘、30 分鐘和 2 小時後重試,最多 5 次。
  • 同一個事件可能會到達兩次:請依 Sofia-Event-Id 去除重複。
  • 盡快回應,事後再處理事件。

POST /v1/webhooks/{id}/test 會傳送一個 webhook.test 事件,簽章方式與其他事件相同。