アプリに組み込む

結果のコールバック

Webhook は、タスクの完了からアプリがそれを知るまでの遅延を短縮します。これは配信の最適化です:3 回試行した後、ゲートウェイは試行をやめます。信頼できる情報源は引き続きステータスエンドポイントです。

#送信時にコールバックを登録

送信リクエストに webhook_url を指定します。webhook_secret を加えるとすべての配信に署名が付きます — 加えなければ、署名は一切付きません。

submit.sh
curl https://www.aisplice.store/v1/videos \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-job-8842" \
  -d '{
    "model": "doubao-seedance-2.5",
    "prompt": "A cinematic product scene",
    "webhook_url": "https://your.app/hooks/ai-tasks",
    "webhook_secret": "whsec_replace_me"
  }'

パブリックにルーティング可能であること

localhost、127.0.0.1、プライベートアドレス帯、リンクローカル、クラウドのメタデータアドレスは、タスク送信時に拒否されます — 配信時ではありません。開発中はトンネルを使用してください。

http または https で、送信時に名前解決できること

ホスト名を名前解決し、返されたすべてのアドレスをチェックします。名前解決できないと送信は失敗します。

配信はベストエフォート

3 回再試行した後、ゲートウェイは配信をやめてログのみ記録します。信頼できる情報源は引き続きステータスエンドポイントです。

localhost はタスク送信時に拒否される

コールバックのホストはタスクが受け付けられる前に名前解決・チェックされるため、プライベートアドレスやループバックアドレスでは送信自体が失敗します — 1 時間後の配信で失敗するのではありません。開発中は webhook_url を公開トンネルに向けるか、指定せずにポーリングしてください。

#ヘッダーとペイロード

ヘッダー送信される条件使い方
X-Xiaoxiang-Signatureタスクを webhook_secret 付きで送信した場合のみ。sha256=<生のボディの HMAC-SHA256(16 進)>。パースする前に、定数時間比較で検証してください。
X-Xiaoxiang-Eventすべての配信。task.completed または task.failed — ペイロードから意図を推測し直さず、この値で振り分けてください。
X-Xiaoxiang-Event-Idすべての配信。同じイベントの再試行間で変わりません。保存して重複を破棄してください — これが重複排除キーです。
フィールド型含まれる条件説明
event_idstring常にX-Xiaoxiang-Event-Id と一致します。これで重複排除してください。
eventstring常に"task.completed" または "task.failed"。
task_idstring常に送信時の呼び出しで返された id — 突き合わせ用のキーです。
statusstring常に動画の語彙による終了ステータス — 画像や音楽のタスクでも "completed" または "failed" です。
modelstring判明している場合タスクを生成したモデル。不明な場合は省略されます。
urlstring成功時タスク成功時の結果 URL。安定したプラットフォームのリンクには /content を推奨します。
fail_reasonstring失敗時失敗時のみ含まれます。
quotainteger常に実際に消費した利用枠(内部単位)。照合にはこのフィールドではなく使用量ログを使ってください。
created_atinteger常にタスク送信時刻(Unix 秒)。
finished_atinteger常に終了状態に遷移した時刻(Unix 秒)。
delivery.json
{
  "event_id": "task_8f21c...:task.completed",
  "event": "task.completed",
  "task_id": "8f21c0d2-...",
  "status": "completed",
  "model": "doubao-seedance-2.5",
  "url": "https://cdn.example.com/renders/8f21c0d2.mp4",
  "quota": 128000,
  "created_at": 1789000000,
  "finished_at": 1789000184
}

#生のリクエストボディを検証

JSON をパースする前に、受信したバイト列そのものに対して HMAC-SHA256 を計算してください — ペイロードを再シリアライズするとバイト列が変わり、比較が失敗します。定数時間で比較し、一致しなければ拒否してください。

import crypto from "node:crypto";

// Express: express.raw({ type: "application/json" }) — not express.json().
export function handleTaskWebhook(req, res) {
  const received = req.get("X-Xiaoxiang-Signature") ?? "";
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
      .update(req.body)              // Buffer of the raw body
      .digest("hex");

  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString("utf8"));
  // Deduplicate first, acknowledge fast, then do the work off-request.
  if (!seen.add(req.get("X-Xiaoxiang-Event-Id"))) return res.status(200).end();
  queue.push(event);
  res.status(200).end();
}

#配信と重複排除

15 秒以内に応答する

これが配信のタイムアウトです。それより遅いと失敗した試行と見なされるため、処理はキューに入れて先に 2xx を返してください。

イベント ID で重複排除する

X-Xiaoxiang-Event-Id は再試行間で変わりません。業務データを変更する前に保存し、重複を破棄してください。

再試行は 3 回、その後は届かない

配信に失敗すると、5 秒後、30 秒後、2 分後に再試行します。その後、ゲートウェイはログを記録するだけです — それ以上は何も届きません。

#照合用のポーラーを維持

Webhook の配信はベストエフォート

データベースでまだ終了状態になっていないタスクに対して、低頻度のジョブを実行してください — 配信期間中にデプロイが 1 回あるだけで取りこぼすことがあります。ステータスエンドポイントを照会して行を修正し、Webhook はあくまで遅延を減らす手段として扱ってください。

まだ解決しませんか?

リクエスト ID かエラーメッセージを送っていただければ、こちらで調査します。