チュートリアル
HiAPIのコールバックが届かない?Webhook配信問題の診断と修復ガイド
POST https://api.hiapi.ai/v1/tasks にタスクを投入する際、callback.url を付けて、タスク完了後にHiAPIからPOSTで通知を受け取りたいと考えていた。しかしタスクは GET /v1/tasks/{taskId} で確認するとすでに success になっているのに、コールバックエンドポイントには一件も届かない。こうした問題の多くはHiAPI側ではなく、コールバックURLに到達できるか、受信側が約束どおり2xxを返しているか、そしてポーリングとコールバックのどちらかが先走っていないかにあります。以下では実際のコールバック契約に沿って項目ごとに切り分け、すぐに実行できる修復手順を示します。
タスクがいつまでも終端状態に到達しない場合(
data.statusが長時間queued/handling/archivingのまま)、それは別種の問題です。HiAPIタスクの停止・タイムアウト診断とリトライガイドをご覧ください。本記事ではタスクは完了済みなのにコールバック通知が届かないシナリオのみを扱います。
問題の症状
典型的なのは、タスク側はすべて正常で GET /v1/tasks/{taskId} が data.status を success(または fail)で返すにもかかわらず、公網にデプロイしたコールバックエンドポイントのログにHiAPIからのPOST記録が一切ないケースです。あるいはたまに受信できて頻繁に取りこぼす、同じ taskId が2回配信されて重複排除ロジックに異常と判定される、といったケースもあります。
まず事実を押さえましょう。コールバックは結果を取得する一手段にすぎず、唯一の経路ではありません。HiAPIの非同期タスクは常に GET /v1/tasks/{taskId} で能動的にポーリングして終端状態を取得できます。コールバック(Webhook)はポーリングを省くためのものにすぎません。したがって「コールバックが届かない」は「結果が取得できない」と同義ではなく、まずポーリングでタスク自体が正常であることを確認し、そのうえで配信問題を特定してください。
コールバックの実際の契約(まず突き合わせ、それから切り分け)
コールバックを登録する際、callback は POST /v1/tasks のトップレベルフィールドであり、model や input と同階層です。input の中に入れてはいけません。
{
"model": "gpt-image-2",
"input": { "prompt": "a calm blue data center" },
"callback": {
"url": "https://your-domain.com/hiapi/callback",
"when": "final"
}
}
必ず覚えておくべき点:
callback.urlは公網から到達可能な http(s) URL である必要があります。HiAPI は公網からこのURLへPOSTを送信します。callback.whenは現在finalのみ受け付けます。タスクが終端状態に到達した時点で1回配信され、successとfailのどちらでもトリガーされます。進捗ベースや段階的なコールバックはありません。- 配信方式は
POST+Content-Type: application/jsonで、リクエストボディはGET /v1/tasks/{taskId}のdataフィールドと完全に一致します。つまりタスク詳細オブジェクトであり、status、output、error、taskId、created、completedなどを含みます。 - 配信失敗(非2xx / タイムアウト / ネットワークエラー)時は 即時 → +1分 → +5分 → +20分 で再配信し、最大4回です。4回とも失敗すると再試行は行われず、タスク自体の状態には影響しません。
- 終端コールバックは少なくとも1回(at least once)のセマンティクスであり、ちょうど1回ではありません。同じ
taskIdが複数回配信される可能性があるため、受信側はtaskIdで冪等な重複排除を行う必要があります。
契約の詳細は公式ドキュメントに準拠します:タスク作成と callback フィールド、非同期API概要(コールバックのリトライと冪等性)。
よくある原因(発生確率の高い順)
1. コールバックURLが公網からアクセス不可(最も多い)
http://localhost:3000/...、内部IP、または内部DNSでしか解決できないドメインになっているケースです。HiAPIは公網からあなたのローカルマシンに到達できないため、当然配信できません。ローカル開発で最も踏みやすい項目です。
2. 受信側が2xxを返さない
ハンドラが例外を投げる、3xxリダイレクトを返す、認証ミドルウェアに401/403でブロックされる、あるいは5xxを返すケースです。HiAPIはHTTP 2xxのみを配信成功とみなし、それ以外はすべてリトライに入り、リトライの時間枠を使い果たすと「届かない」という症状になります。
3. 処理タイムアウト
ハンドラ内で重い処理(成果物のダウンロード、オブジェクトストレージへの転送、DB書き込み、メッセージ送信)を先に行ってから200を返すと、時間がかかりすぎてタイムアウト判定されリトライが発生します。これが重なるとパケットロスのように見えます。
4. callback を input の中に入れてしまった
これはトップレベルフィールドであり、input に入れてもコールバック登録として認識されません。さらに各モデルの input は厳格なスキーマであるため、未知のキーを追加すると通常は投入時に同期的にエラー invalid input: <root>: additional properties 'callback' not allowed が返り、タスク自体が投入成功していません。
5. when に final 以外の値を指定した
現在は final のみサポートしており、それ以外は拒否されます。
6. ポーリングとコールバックの先走り
ポーリングとコールバック待ちの両方を有効にしていると、ポーリングが先に終端状態を取得してローカルのタスクレコードを閉じてしまい、コールバック到着時に対応するレコードが見つからず「受信していない」と扱われるケースです。
7. 証明書やWAFによるブロック
コールバックURLに自己署名/期限切れ証明書を使っている、あるいはWAFが通常のUAや自社の認証ヘッダーを持たない匿名POSTを疑わしいトラフィックとしてブロックしているケースです。
修復手順
- まずタスク自体が正常であることを確認する。
GET /v1/tasks/{taskId}でdata.statusを確認し、success/failならタスクは終端状態であり、問題は配信段階にあります。非終端状態のままなら別種の問題です(冒頭の停止診断を参照)。 - コールバックURLを公網のhttpsドメインに変更する。外部マシンから
curl -i -X POST https://your-domain.com/hiapi/callback -d '{}'を実行し、疎通できて2xxが返ることを確認します。localhostでのデバッグは、公網トンネルでローカルポートを外部にマッピングしてください。 - コールバックを確実に記録してから素早く2xxを返す。リクエストを受けたらまずコールバック内容をDBまたは永続キューに書き込み(この工程は高速に)、書き込み成功後に2xxを返します。ダウンロードや転送といった重い処理はバックグラウンドで非同期に行います。DB保存前に200を返してはいけません。直後にプロセスがクラッシュするとこのコールバックは失われ、HiAPIは2xxを受け取ると再配信しないためです。同時に、HiAPIのコールバックは自社の認証ヘッダーを持たないため、そのパスは認証ミドルウェアから除外してください。
callbackの位置と値を確認する。リクエストボディのトップレベル(model、inputと同階層)にあり、urlが公網のhttp(s)、whenがfinalであることを確認します。taskIdによる冪等な重複排除を行う。コールバックを受けたら、まずローカルでこのtaskIdを処理済みか確認し、未処理ならDB保存/後続アクションのトリガーを行います。at-least-onceセマンティクスでは重複到達は正常であり、重複を異常として捨てないでください。- フォールバックのポーリングを追加する。想定時間を超えてもコールバックが届かない
taskIdについては、能動的にGET /v1/tasks/{taskId}で終端状態を補完取得します。コールバックは最適化、ポーリングは最低ラインであり、両方を同じ冪等ロジックでまとめます。 - 成果物を速やかに転送する。コールバックボディの
output[].urlは一時リンク(storageがtemp、expireAtはUnix時間で約7日後に期限切れ)です。取得後すぐにダウンロードするか、自分のストレージへ転送し、ホットリンクのまま保存しないでください。
最小検証例
まずコールバックには触れず、「投入 + ポーリング」でキーと接続自体が通っていることを証明します(素のモデルIDを使用):
# 1. タスクを投入
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","input":{"prompt":"a calm blue data center"}}'
# → {"code":200,"message":"success","data":{"taskId":"tk-hiapi-..."}}
# 2. 返された taskId で終端状態をポーリング
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
# → data.status が queued/handling/archiving から success(output 含む)または fail(error 含む)へ遷移
投入時に HTTP 401 + {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}} が返る場合はAPIキーが誤っています。コンソールで確認してから再試行してください。接続が問題ないことを確認したら、POST ボディにトップレベルの callback を追加し、上記の修復手順に沿って受信側を項目ごとに確認します。
FAQ
コールバックがずっと届かないが、タスクは実際に成功している場合、結果は取得できますか?
取得できます。いつでも GET /v1/tasks/{taskId} で data.status と data.output を読めます。コールバックの喪失は能動的な結果取得に影響しません。
コールバックは何回、どれくらいの間隔でリトライされますか?
非2xx / タイムアウト / ネットワークエラー時は 即時 → +1分 → +5分 → +20分 で再配信し、最大4回です。4回とも失敗すると再試行は行われず、タスク自身の success/fail 状態にも影響しません。
同じタスクでコールバックを2回受信するのは正常ですか?
正常です。終端コールバックは「少なくとも1回」のセマンティクスであり、taskId で冪等に重複排除すればよく、重複配信をエラーとして扱わないでください。
ローカル開発でコールバックを受け取るには?
localhost や内部アドレスにはHiAPIから到達できません。公網トンネルでローカルポートを公網にマッピングするか、ローカル段階ではポーリングに切り替え、リリース時にコールバックへ切り替えてください。
コールバックリクエストボディにはどんなフィールドがありますか?
GET /v1/tasks/{taskId} の data と完全に一致し、taskId、status、output(成功時の成果物配列)、error(失敗時の code と message)、created、completed などを含みます。詳細はタスク詳細ドキュメントをご覧ください。
コールバックが届かないと余分に課金されますか? されません。タスク失敗自体は課金されず、予約枠は返却されます。コールバックの配信成否もタスクの課金結果を変えず、課金はタスクが最終的に生成成功したかどうかだけで計算されます。
関連記事
すべての記事を見る →-
チュートリアル
HiAPI API で hailuo-2.3-fast/image-to-video を呼び出す方法:curl と Python の実践
高速・低コストな画像から動画へのティアにおける最初の実行可能なリクエスト — モデル ID から MP4 のダウンロードまで
-
チュートリアル
HiAPI APIでhailuo-2.3/text-to-videoを呼び出す方法:curl、Python、実行可能なリクエスト例
-
チュートリアル
HiAPI で Kandinsky 6.0 Pro のテキストto動画を使用する:curl と Python チュートリアル
リクエスト優先の実践デモ:プロンプトから動画クリップへの呼び出しを構築し、リクエストを送信し、完了状態をポーリングし、結果をダウンロードする