目次
先に決める:短い待ち時間か、長い処理か
Responses APIの通常リクエストは、レスポンスを待つ接続が切れると、クライアント側では処理結果を把握できないことがあります。処理が長くなり、HTTP待ち時間に依存したくない場合は、background modeでResponseを非同期に作成し、Response IDを使って状態を確認します。
完了通知を受けたいならwebhook、少ない構成で状態を追いたいならpollingが選択肢です。どちらを選んでも、受け取った通知や作成時の応答だけを唯一の記録にしないでください。ID・現在状態・最終結果をアプリケーション側で追跡し、結果を保存できたことまでを完了条件にします。
背景処理は「リクエストを送った」時点では終わっていません。画面を閉じたりworkerを再起動したりしても再開できるよう、Response IDと業務上のjob IDの対応を先に永続化します。
最小の状態管理モデル
まず自分のシステムのjobとOpenAI Responseを別々に記録します。
表は横にスクロールできます
| 自システムの項目 | 目的 |
|---|---|
| job_id | 利用者が作成した処理を識別する固定ID |
| response_id | Responses APIが返すResponse ID |
| requested_at | API requestを開始した時刻 |
| last_status | 最後に取得したResponse status |
| last_checked_at | pollingまたはwebhook後に取得した時刻 |
| terminal_at | terminal stateを確認した時刻 |
| result_ref | outputを安全に保存した場所、または結果レコード |
| webhook_event_ids | 受信したwebhook-idと処理済み状態 |
| error_summary | 利用者へ表示可能な要約。secretや不要な本文は含めない |
作成処理の流れは次のとおりです。
-
アプリケーション内で一意のjob_idを作る。
-
Responses APIへbackground=trueでリクエストする。
-
Response IDと初期状態をjob_idに結び付けて保存する。
-
保存が完了してから、pollingまたはwebhook待ちへ移る。
-
final stateを取得したら、必要なoutputを検証して保存する。
-
result_refとterminal statusを永続化し、利用者へ完了を示す。
API応答の受信前にプロセスが落ちた場合はResponse IDを記録できていない可能性があります。逆に、Response IDを保存した後にworkerが落ちた場合は、保存済みIDからretrieveを再開できます。この2つは復旧方法が異なるため、内部状態を「送信中」「ID記録済み」「結果回収中」などに分け、タイムアウトだけで失敗確定にしません。
Pollingで状態を追う
公式ガイドでは、background ResponseをGETで取得して状態を確認します。statusがqueuedまたはin_progressの間は確認を続け、これらを抜けた時点でterminal stateに到達したものとして扱います。固定回数を無条件で繰り返すのではなく、処理時間、API負荷、アプリケーションのSLOに合わせて間隔と終了条件を設計します。
基本手順:
-
Response作成直後にresponse.idとresponse.statusを保存する。
-
次回確認時はそのIDを使ってResponses APIから最新Responseを取得する。
-
queued / in_progressなら、設定した待ち時間の後に再取得する。
-
それ以外のstatusならterminal stateとして分類する。
-
成功状態ならoutputを読み、業務上の必須項目・形式を検証して保存する。
-
failed / cancelledなど成功以外のterminal stateなら、状態とエラー情報を記録し、利用者へ再実行や修正が必要かを案内する。
これは概念的な状態遷移です。個々のstatus値やエラーobjectの形はAPI referenceと利用中のSDK versionで確認してください。statusの文字列を独自に想定し、未知の状態を成功へまとめないことが大切です。未対応statusを受けたら、raw responseを安全な範囲で保存し、処理を保留して調査できるようにします。
例として、2秒間隔で取得するサンプルが公式ガイドにありますが、その値をすべてのワークロードへ適用する必要はありません。処理数が増える場合は、固定頻度の大量pollingが不要なAPI呼出しを増やします。初回は短く、その後は待ち時間を段階的に延ばす設計や、期限・最大試行数を設ける方法を比較します。レート制限やSDKのretry behaviorは、実際のendpoint・project・SDKに合わせて別途確認してください。
pollingのみで構成すると、webhook serverを用意せず状態を取得できます。その代わり、Responseの完了まで定期的にAPIを呼び、遅延と呼出しコストを受け入れる必要があります。完了時刻の即時性が重要でなければ、監視workerが定期的に未完了IDを再取得する方式がシンプルです。
Webhookで完了を受け取る
Webhookは、処理完了などのイベントが起きたときに、設定した公開URLへPOSTを届ける仕組みです。Responsesのbackground処理と組み合わせれば、通知を契機にResponse IDを取り出し、APIから最終Responseを取得できます。
ただしWebhookは結果本文を永続化する場所ではなく、状態確認を始める合図として扱います。通知が遅れたり重複したりしても業務結果が壊れないようにします。
受信処理の基本:
-
raw request bodyと必要なheadersを保持する。
-
OpenAIの署名secretで署名を検証する。
-
署名が不正ならイベント処理を拒否する。
-
webhook-idを使って重複イベントを識別する。
-
response IDとevent typeを読み、job_idとの対応を確認する。
-
必要な処理をqueueへ登録し、HTTP handlerはすばやく2xxを返す。
-
worker側でResponseをretrieveし、status・output・業務検証を行う。
-
成果とイベント処理済み状態を冪等に保存する。
署名検証前にJSON parserがbodyを変更すると、SDKの検証処理に渡す値が変わる実装があります。利用するSDKのWebhook helperが要求するraw bytes / raw textの形式を確認し、bodyを解析する前に検証します。secretは環境変数やsecret storeから読み、ソース、ログ、クライアント側コードへ出しません。
OpenAIの公式ガイドは、endpointが数秒以内に成功(2xx)を返さない場合や2xx以外を返した場合、deliveryを最大72時間、指数バックオフで再試行すると説明しています。まれに同じイベントが重複することもあり、webhook-idを冪等キーとして利用できます。したがって、重い処理をHTTP handlerの中で完了させようとせず、検証後に内部queueへ記録し、先に受付応答を返す設計が適しています。
受信eventの保存とjob状態更新を同一処理にできない場合は、イベントIDを先に記録し、workerが後から処理を再開できるようにします。同じwebhook-idが再送されたとき、重複jobを作らず、既存の処理結果または処理中状態を返せるようにします。
Webhookとpollingを組み合わせる
実運用では「Webhookだけ」または「Pollingだけ」に固定する必要はありません。Webhookを主な起動手段にし、一定時間イベントを受け取れなかったjobをreconciliation workerがpollingする設計も可能です。
この構成では、次の二つの流れが同じjob recordへ到着します。
-
webhook: イベント受信 → 署名検証 → 重複判定 → workerへenqueue → Response retrieve
-
polling fallback: 未完了job検索 → Response retrieve → status再判定
どちらもResponseのstatusと業務jobの現在状態を比較してから更新します。後から届いた古いイベントが、すでに保存した成功状態を「処理中」へ戻さないよう、状態の単調性と更新条件を定義します。たとえばterminal stateを保存したjobは、同じResponseの遅延通知を受けても結果を書き換えません。異常系の再処理が必要なら、既存jobを巻き戻すのではなく、新しいattempt IDを作るか、明示的な再実行履歴を残します。
reconciliation workerでは、Response IDがあるのに最終状態を記録できていないjob、webhookを受け取ったがretrieveが失敗したjob、イベント欠落が疑われる長時間処理を対象にします。API取得が一時失敗した場合の再試行間隔・最大経過時間・dead-letter行きの基準を決め、無期限のループを避けます。
保存・保持条件を契約前に確認する
Background modeには、通常の応答取得とは異なる保存上の注意があります。現行OpenAI APIガイドでは、Zero Data Retention(ZDR)projectからのbackground requestはstore=falseとして実行され、非同期処理とpollingのためResponseデータが概ね10分間一時保存されると説明しています。また、Modified Abuse Monitoringを使うprojectでは、background Responseをpolling後も保持するにはstore=trueを明示する必要があり、省略またはfalseでは概ね10分後に削除されると案内されています。
この記述だけから、すべての組織・契約・データ種別に同じ保持条件が適用されると推測しないでください。自分のprojectでZDRまたはModified Abuse Monitoringが有効か、storeの扱いは何か、保存後の削除要件はどうなるかを、管理設定と契約条件で確認します。重要な結果をResponse APIだけに長期間残す前提にせず、必要な場合は自社側の保存先・暗号化・アクセス権・retention policyを設計します。不要になった機密出力を自動削除する契約・運用も確認します。
実務では、作成時にstore=trueを付ければ無期限に保持できる、という意味ではありません。APIでResponseを取得可能な期間、OpenAI側のデータ保持ポリシー、project契約、ZDR/MAM設定、自社が保存するデータを別々に確認してください。条件が読めないときは、response IDだけ保存すれば安全だと判断せず、Privacy・法務担当へ確認します。
よくある失敗と復旧
表は横にスクロールできます
| 症状 | 起こり得る状態 | 安全な確認 |
|---|---|---|
| API作成後に画面がtimeout | Response作成済みか不明 | client timeoutを即「未作成」とせず、保存できたID・request logを確認。IDがあればretrieve |
| pollingがqueuedのまま | まだ受付待ち、または処理進行を観測できていない | 時間上限・間隔を確認し、別jobへ同一処理を二重作成しない |
| webhookを受け取れない | URL・delivery・server response・署名処理の問題など | dashboardのevent設定、到達ログ、status code、raw bodyの扱いを確認。並行してResponse IDのpollingを行う |
| webhookが重複した | delivery再試行やduplicate event | webhook-idとjob_idで重複排除し、同じ出力を二重保存しない |
| webhook後のretrieveが失敗 | event受信とResponse取得は別処理 | eventを受付済みとして記録し、retrieveを内部queueで再試行。期限後の取得可能性は保存条件で確認 |
| terminalだが業務データが欠落 | API処理は終了したが成果物検証が不足 | status成功だけで確定せず、schema・必須列・件数・業務ルールを検査 |
| store=falseで後から取得できない | background出力の保持期限を過ぎた可能性 | 自社側に保存した結果・event・job記録を確認。再生成が必要かを担当者が判断 |
復旧のために無差別に同じpromptを新規送信すると、二重処理や二重課金、二重書き込みにつながることがあります。まずjob_idからattempt、Response ID、保存先、イベント履歴を検索します。結果が確定していれば再送せず、取得できない場合は、元処理の再実行が安全かを確認してから新しいattemptを作成します。
選択表:どの方式にするか
表は横にスクロールできます
| 条件 | まず検討する方式 | 主な注意点 |
|---|---|---|
| 少量の長時間処理、server endpointを持たない | Polling | 定期API呼出し、待ち時間、timeout管理 |
| 完了を早く検知したい、公開HTTPS endpointを運用できる | Webhook | 署名、2xx応答、重複排除、delivery監視 |
| 完了通知の取りこぼしが心配、結果が業務上重要 | Webhook + polling reconciliation | 同一jobの冪等更新、状態の巻き戻し防止 |
| 出力を長く保持できない設定・契約 | 完了後すぐ自社保存 | store / retention条件、保存先のprivacyと削除要件 |
| API応答を受け取れず処理成否が不明 | 既存IDのretrieve / readback | 新規送信の前に二重実行を防ぐ |
実装前チェックリスト
-
background=trueでResponseを作成する処理と、通常同期処理を区別したか。
-
Response IDをjob_idと一緒に永続化しているか。
-
queued / in_progressを処理中と扱い、terminal stateを成功・失敗・cancel等へ分けているか。
-
Webhook署名をraw bodyで検証し、secretを安全に保持しているか。
-
webhook-idで重複を排除し、短時間で2xx応答するか。
-
Webhookとpolling fallbackが同じ業務jobを二重確定しないか。
-
Response ID、output、event ID、必要なerrorを保存する期間・アクセス権を決めたか。
-
ZDR / Modified Abuse Monitoring / store条件をprojectで確認したか。
-
結果をAPIから取得できる期間を前提に、必要outputを自社側へ保存するか。
-
statusが成功でも業務成果物の完全性を別に検証するか。
-
OpenAI APIと実装SDKのversion、response schema、再試行方針をtest環境で確認したか。
公式情報と確認範囲
2026-10-15にOpenAI公式API資料を確認しました。Background guideは非同期作成、polling状態、queued/in_progressからのterminal state、ZDR/MAMとstoreに関する概ね10分の保持説明を示しています。Webhooks guideは署名検証、2xx応答、最大72時間のdelivery retry、webhook-idによる重複排除を説明しています。
出典・適用範囲
対象はOpenAI Responses API background modeとproject webhooksです。利用可能な機能、data retention、store要件はproject設定・契約・データ管理条件に依存します。本稿ではOpenAI APIを実呼出ししていません。Webhook endpoint、署名検証、SDK例、重複処理、polling intervalは利用SDKと運用環境でtestしてください。API出力の保持を業務上必要とする場合は、最新の契約・管理設定と自社のprivacy要件を別途確認してください。
