A user starts a report, closes the tab, and returns ten minutes later. The response may have finished, failed, or been cancelled while your frontend knew nothing. OpenAI background mode keeps the provider task alive; your application still needs to recover the truth.
The reliable design starts with a local job state machine. It treats a webhook as a delivery hint, not as permission to run the same side effect twice.
Map OpenAI background mode onto your own job states
The Responses API reference exposes a background flag for work that should continue after the initial request. The provider returns a response identifier; store it beside your internal job ID, account ID, requested operation, and idempotency key.
Create one durable job row before you call the API, then attach the provider response ID to that row. A practical state set is queued, submitted, running, completed, failed, cancelled, and timed out. Keep the provider status and your product status separate so a completed model response cannot bypass a later policy or review step.
One request, four boundaries
- Your API authenticates the user and writes the job.
- The worker submits one background response with an idempotency key.
- The webhook receiver verifies and records the delivery without doing heavy work.
- A reconciler fetches the response and advances product state once.
Return the local job ID to the client. The client can poll your API without learning a provider credential or assuming that a browser connection controls the job lifetime.
Verify delivery before you parse it
OpenAI’s webhook endpoint API returns a signing secret when an endpoint is created. Store that secret in your secrets manager and rotate it through a deployment procedure that supports both old and new values during the cutover.
Verify the signature against the raw request body before parsing JSON, then deduplicate on the webhook event ID. A valid duplicate should return a quick success response. A valid new event should enter a queue; it should not perform a billing change, send a final email, or write a customer-visible result inside the HTTP handler.

The event reference separates completed, failed, and cancelled response events. Persist the event type and arrival time, then retrieve the response by ID. That readback protects you from trusting a partial payload or a stale local assumption.
Let polling repair webhook loss
Webhooks cross DNS, TLS, load balancers, deployments, and queues. Each layer can delay or drop a delivery. Run a reconciler that scans submitted or running jobs whose last update exceeded a short threshold, retrieves their provider state, and applies the same transition function used by webhook processing.
Use one transition function for both paths. If the webhook and poll arrive together, a conditional database update should let one worker win and the other become a no-op. Record the losing attempt so operators can distinguish healthy duplication from repeated failures.
Before and after the durable boundary
Fragile: the browser waits, a webhook writes the final artifact, and a retry can create a second result.
Recoverable: the app owns one job, deliveries only trigger reconciliation, and the result advances once.
Test cancellation and retention as product behavior
Cancellation has two clocks. Your user can ask to stop, while the provider response may already be completing. Mark the local job cancel-requested, call the cancellation endpoint where supported, and refuse downstream side effects unless the reconciled state still permits them.
OpenAI’s data controls table says background mode keeps response data for roughly ten minutes to enable polling and is not compatible with Zero Data Retention. Teams with a ZDR requirement should treat that as a design constraint, not a toggle to discover during review.
Run five failure drills: drop the webhook, replay it, delay it past the polling threshold, cancel during completion, and return an invalid signature. Your evidence should show one final job state and no duplicate customer action.
Keep the operator view small
Operators need the internal job ID, provider response ID, tenant, current states, attempt count, last verified event, retention class, and next retry time. They do not need the full prompt or sensitive output in a general queue dashboard.
Alert on stuck-state age and repeated reconciliation failure. A single delayed webhook is routine. A growing set of jobs that cannot retrieve provider state is an incident.
Own idempotency on both sides of completion
Submission idempotency prevents two provider responses when a worker loses the first HTTP result and retries. Completion idempotency prevents two product actions when webhook delivery, polling, or queue redelivery presents the same finished response again.
Use different keys for those boundaries. Bind the submission key to the user’s requested job and input hash. Bind the completion key to the internal job and the one downstream effect, such as storing a report version or sending a delivery notice.
Keep the original response and later product transformation separately addressable. An operator should be able to replay a failed formatter or notification without asking the model to do the expensive work again.
The Neyrotex Agents API checklist covers harness and tool controls. Our GPT-Live-1 architecture guide separates a realtime voice loop from durable actions. Follow the AI Tech hub for API changes, then use the prompt-injection security checklist before release.