ガイド

Webhook

バッチジョブの完了通知を受け取る

1 分を超えるバッチジョブでは、Webhook はポーリングよりも安価で高速です。Thunderbit はジョブが終了状態に達したときに、あなたの URL に POST します。

送信時の設定

{
  "urls": ["https://example.com/page1"],
  "webhook": {
    "url": "https://your-server.com/api/webhook/distill",
    "secret": "whsec_your_secret_key",
    "headers": { "X-Custom-Auth": "your-token" }
  }
}

webhook.headers フィールドはカスタムヘッダーのマップを受け付けますが、このフィールドは将来用に予約されており、現在サーバーから送信されていません。検証は下記のヘッダーのみを前提にしてください。

リクエストヘッダー

すべての配信に以下の 4 つのヘッダーが含まれます:

HeaderValue
Content-Typeapplication/json
X-Webhook-Eventbatch.completed
X-Webhook-TimestampUnix エポック(ミリ秒
X-Webhook-Signaturesha256=<base64-encoded HMAC-SHA256>

ペイロード

{
  "id": "batch_a1b2c3d4...",
  "jobType": "batch_distill",
  "status": "COMPLETED",
  "total": 50,
  "completed": 49,
  "failed": 1,
  "creditsUsed": 100,
  "createdAt": "2026-04-26T10:00:00Z",
  "completedAt": "2026-04-26T10:05:23Z"
}
  • jobTypebatch_distill または batch_extract
  • statusCOMPLETEDFAILEDCANCELLED のいずれか。

ペイロードは意図的に小さく保たれています。コールバックを受け取った後、GET /batch/distill/{id}(または /batch/extract/{id})で完全な結果を取得してください。

署名検証

secret が設定されている場合、すべての配信に X-Webhook-Signature ヘッダーが付与されます。

  • アルゴリズム:HMAC-SHA256
  • 署名対象文字列:<X-Webhook-Timestamp>.<raw-json-body>(リテラルの . を挟む)
  • 出力形式:sha256=<base64-encoded-hash>(Base64、hex ではありません

のリクエストバイトで検証してください(JSON を再シリアライズしないこと)。比較は定数時間で行います。

import hmac, hashlib, base64

def verify(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
    base = f"{timestamp}.{raw_body.decode('utf-8')}".encode('utf-8')
    digest = hmac.new(secret.encode('utf-8'), base, hashlib.sha256).digest()
    expected = "sha256=" + base64.b64encode(digest).decode('ascii')
    return hmac.compare_digest(expected, signature)

リプレイ攻撃対策

|now - X-Webhook-Timestamp| > 60000(60 秒のウィンドウ)の場合、リクエストを拒否してください。タイムスタンプは Unix エポックの ミリ秒 です。本番環境で署名のない webhook を決して信頼しないでください。

リトライ動作

  • 1 回あたりのタイムアウト:30 秒
  • 最大リトライ回数:3
  • バックオフ:指数バックオフ、開始 1 秒、上限 10 秒
  • エンドツーエンドの合計ウィンドウ:約 120 秒

すべてのリトライが失敗しても、ジョブは弊社側では完了済みです。受信エンドポイントは冪等にしてください(id を重複排除キーとして使用)。