01
キーなしの再送は二枚目の請求書です
Idempotency-Key があれば、24 時間以内の同じモデル・入力・コールバックの再送は、資金を二度保留せず元のタスクを返します。スコープは API キー単位で、兄弟キーが同じ値を再生しても 409 が返るだけでタスク ID は得られません。
実装ガイド
メディアタスクは、それを作った HTTP 呼び出しよりも、起点となったページよりも、しばしばそのとき動いていたデプロイよりも長く生き残ります。ここはそれに耐える作り方の話です。

解決する課題
送信からファイル入手までに、作業を失う場所が四つあります。タイムアウト後に何も考えず再送する作成、検証できないコールバック、叩きすぎるか諦めるポーリング、そして扱いを決めているあいだに期限が切れた成果物 URL です。
01
Idempotency-Key があれば、24 時間以内の同じモデル・入力・コールバックの再送は、資金を二度保留せず元のタスクを返します。スコープは API キー単位で、兄弟キーが同じ値を再生しても 409 が返るだけでタスク ID は得られません。
02
配信は X-Webhook-Timestamp と、taskId.timestamp.sha256(raw_body) から計算した Base64 の HMAC-SHA256 を X-Webhook-Signature で運びます。生のバイト列に対して定時間で比較し、古いタイムスタンプはタスクや課金状態に触れる前に拒否してください。
03
recordInfo は補助であって転送路ではありません。二秒程度から始め、毎回 1.5 倍し、十五秒で頭打ちにし、ネットワークではなく自分が決めた期限で止めます。
実装方針
冪等キー、taskId、自分の相関 ID を、呼び出し元へ応答するのと同じトランザクションで永続化します。以後のコールバック、ポーリング、再試行、問い合わせはすべてその行に解決し、重複配信は二件目の注文ではなく一回の更新になります。
先に検証し、それから読む。署名は生のバイト列を対象にするため、比較が通ってから解析します。
Verify the callback before you trust it
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
// rawBody is the exact bytes that arrived. Parse only to read the task ID:
// the digest is always computed over those raw bytes, never over a re-encode.
export function verifyDelivery(rawBody, headers, webhookKey) {
const timestamp = headers["x-webhook-timestamp"];
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const taskId = JSON.parse(rawBody).data.taskId; // payload version 2
const digest = createHash("sha256").update(rawBody).digest("hex");
const expected = createHmac("sha256", webhookKey)
.update(`${taskId}.${timestamp}.${digest}`)
.digest("base64");
const sent = Buffer.from(headers["x-webhook-signature"] ?? "", "utf8");
const mine = Buffer.from(expected, "utf8");
return sent.length === mine.length && timingSafeEqual(sent, mine);
}Delivery headers
X-Webhook-Timestamp: 1788652800
X-Webhook-Signature: <base64>
X-Webhook-Payload-Version: 2
{
"code": 200,
"msg": "success",
"request_id": "whd_01k3m8x9q2z4v7n5p6r8s0t1w3",
"data": { "taskId": "job_01k3…", "state": "succeeded", "settled": true, … }
}契約
| フィールド / ヘッダー | 値・上限 |
|---|---|
| Idempotency-Key | 24 h · scoped to one API key |
| X-Webhook-Signature | base64(HMAC-SHA256) |
| signed string | taskId.timestamp.sha256(raw_body) |
| X-Webhook-Payload-Version | 2 for new integrations |
横にスワイプして全項目を確認できます。
導入
向き不向き
裏付け
/api/v1 の全操作で封筒は {code, msg, data, request_id} です。ペイロード版 2 では配信の request_id が再送をまたいで不変なので、そのまま重複排除キーになります。
settled: false の cost が保留中の見積額、settled: true が最終請求です。成功時に保留額を超えることはなく、失敗や期限切れでは全額解放されます。
errorCode は九つの安定値のいずれかです。invalid_request、unsupported_combination、content_rejected、rate_limited、upstream_unavailable、generation_failed、timeout、invalid_asset、upstream_failed。それ以外は upstream_failed として扱ってください。
公開前に
よくある質問
モデル
各リンクはそのモデルシリーズのページに移動します。入力、上限、コンテンツ方針、単位あたりの現在価格をそこで確認できます。
次に
長時間のメディアジョブをサーバーから送り、確実に終わらせる。冪等キー、署名付きコールバック、上限付きポーリング、五状態の状態機械。