GPT Responses Background Jobs: Poll, Cancel, and Recover Without Duplicate Work
A report request leaves your application, the browser closes, and the next worker cannot tell whether GPT is still working. Restarting the request feels helpful until two expensive reports appear. Background generation changes this problem: the application can receive a response identifier and manage the work through a lifecycle. The useful deliverable is therefore not a longer HTTP timeout. It is a durable record that connects one business request to one upstream response and tells every worker what action is permitted next.
This walkthrough builds that boundary around the GPT Responses API. The example task is a synthetic operations report, with no external tools or business mutations. We submit it, persist its identifier, retrieve its status, and request cancellation. The same pattern fits research summaries and lengthy analysis jobs. It complements the existing Responses tool-call migration tutorial; changing an endpoint and managing the lifetime of an accepted job are separate engineering tasks.
The job record is the product contract
OpenAI's background-mode guide documents asynchronous creation with background=true, followed by retrieval while the response is queued or running. Your interface should expose an application job identifier, not make the upstream identifier its authorization mechanism. Associate the job with its owner, the submitted input version, model identifier, creation time, and response identifier. An authenticated user may inspect or cancel only the application jobs that belong to them.
Keep provider state and application state in separate columns. A response can be complete while its output is awaiting validation. A customer can ask to stop while the upstream cancellation request has not yet returned. A worker can lose connectivity while the response continues. If all three situations are recorded as “failed,” the next operator cannot choose a safe recovery action. Use explicit application labels such as submission uncertain, awaiting review, and cancellation requested, while retaining the provider's unmodified status alongside them.
| Observation | Application action | Do not infer |
|---|---|---|
| queued or in_progress | Schedule another retrieval | A missing result means failure |
| completed | Persist output, then validate | Business acceptance is automatic |
| failed or incomplete | Preserve error or incomplete details | Partial text is an approved report |
| cancelled | Close the generation attempt | Previous external actions were reversed |
| Transport failure | Retain the last known state | The upstream job stopped |
The response retrieval reference defines the returned response object. In your database, store its identifier and status before scheduling the next action. A transition log can answer who requested cancellation and which worker last observed completion. The broader discussion of session event sourcing supplies useful persistence context, but this report job needs only a small, understandable transition history rather than an entire agent platform.
Submission has one dangerous gap
There are two different timeout questions. Did the request connection fail before the application learned the response identifier? Or did a later retrieval fail after the identifier was already saved? The second case is recoverable by retrieving the same response again. The first is ambiguous: the provider may have accepted the request. A general retry wrapper that repeats every POST can turn uncertainty into duplicate work. Record an attempt before submission, and place an unresolved submission in a separate reconciliation queue.
The following native reference program deliberately sends one creation request. It uses separate submit, poll, and cancel commands so that a process restart does not automatically create another response. Install the OpenAI Python SDK, set OPENAI_API_KEY and OPENAI_MODEL, and choose a model whose current documentation supports background execution. Set JOB_FILE to a private path for this demonstration. This file is a single-worker teaching device; a service should use a database transaction and a lease or equivalent concurrency control.
The local polling budget and interval are example choices, not provider service guarantees. Reaching the budget stops this process, not the model. Run poll again to continue observing the same identifier while retrieval remains available. Connection errors during retrieval leave the saved record intact. Production scheduling should persist the next polling time and release the worker, rather than sleeping through every waiting period. This keeps report generation from occupying all of the application's request workers.
Saving the complete response makes this example easy to inspect, but a production retention policy may permit only selected fields and an encrypted output artifact. Do not put response bodies into ordinary access logs. Keep operational logging small: application job, response identifier, endpoint family, elapsed time, status, and request identifier when supplied. The existing API observability guide can help organize those signals without making the model's private input a debugging dependency.
Concurrency creates another failure mode after submission succeeds. Suppose one worker retrieves a completed response while another is still processing an earlier in-progress observation. If the older observation writes last, the database can appear to move backward. Use a transaction or conditional update that prevents a terminal observation from being replaced by an active one. Preserve the observation history when diagnosing the race, but let one authoritative state drive the next scheduled action. The local file example assumes a single operator and deliberately does not solve this distributed coordination problem.
Artifact publication needs similar ownership. Claim a completed job before writing its report, and record the published artifact identifier before acknowledging delivery. If the worker crashes after storage succeeds, a replacement worker should find that artifact instead of creating a second customer-visible report. A stable application job identifier is a useful storage key, while a separate attempt identifier tracks legitimate regenerations. These identifiers solve different problems: the first groups the customer's request, and the second explains which upstream execution produced an output.
Test recovery by stopping the local poller after the response identifier has been written, then starting it again with the same job file. Test the submission gap separately with a simulated connection exception; do not create deliberate paid duplicates to demonstrate it. Your expected result is an unresolved attempt that requires reconciliation, not an automatic second POST. These controlled failure cases reveal whether the application respects uncertainty before a production incident forces an operator to decide under pressure.
Cancellation is a race you must display honestly
OpenAI provides POST /responses/{response_id}/cancel for responses created in background mode. A user's stop action should first be recorded locally, then sent to that endpoint. Treat the returned object as an observation. Do not stamp the job cancelled merely because the button was clicked. If generation completes between the click and the provider request, preserve the actual final state and apply your own rule about whether to deliver the completed report.
For this report product, a sensible rule is to suppress automatic delivery once the user has withdrawn the request, even if upstream computation finishes successfully. That rule belongs to the application, and it should be recorded independently from provider completion. Another product may allow a user to retrieve the already completed artifact manually. Decide this before the interface ships; otherwise a cancellation race becomes a dispute about whether the system ignored the user.
A cancellation timeout also leaves uncertainty. Retrieve the existing identifier to establish its current state before choosing another action. The official error guidance helps distinguish request failures, authentication issues, and temporary service conditions. Apply a bounded retry policy to observations. Avoid turning an error from cancellation into a fresh generation request. If future versions add tools that mutate external systems, stopping generation still cannot serve as a rollback procedure for those systems.
Storage settings change recovery design
Current documentation, checked on November 7, 2026, allows background requests with store=false. The response data is nevertheless temporarily stored to support asynchronous execution and polling, for roughly ten minutes. OpenAI's data controls documentation distinguishes this temporary polling storage from longer retention, and describes how explicit store=true affects background retention. Review the rules for your project configuration. Do not reuse an older integration assumption that background mode always requires permanent response storage.
Operationally, a short recovery window means your application should persist the result as soon as it observes completion. An overnight scheduler that expects to collect every result the following morning is a poor match for temporary retrieval. If a worker returns after the available window and cannot retrieve the response, show an unresolved or expired result rather than inventing success. Recreating the work should be a new, visible application attempt linked to the original request, with a reason recorded.
Local storage has its own lifecycle. Deleting a provider response or allowing its retrieval period to end does not delete your database copy, a downloaded report, or a customer's export. Assign retention periods to each artifact class and keep access controls on the final report. The discussion of API data boundaries is relevant here because a useful background-job record must be durable without becoming an unrestricted archive of every sensitive prompt.
Completion still needs an acceptance gate
When a response is completed, inspect its output before handing it to downstream consumers. A model-generated operations report can omit a requested section, misunderstand the synthetic incident, or include unsupported assertions while the request itself succeeds. Preserve provider error and incomplete details for other terminal states. Do not blindly index the first output item as text: the text generation guide explains that Responses output can include different item types. Use the SDK's text helper or explicitly select text message content.
If the input includes large documents, separate payload acceptance from execution state. A rejected creation request has no accepted background job to poll. The existing Responses payload-size troubleshooting article addresses that earlier boundary. In a release test, include a malformed request, an ordinary completion, a manually cancelled job, and a worker restart after identifier persistence. These are proposed acceptance cases, not claims that the example has been exercised against a paid endpoint.
At higher traffic, webhooks can reduce the need to poll continuously. OpenAI's webhook documentation covers event delivery and signature verification. Treat an event as a trigger to reconcile the application job, and make artifact publication idempotent. A polling worker and a webhook handler may notice the same completion; the report should still be published once. Keeping that rule in the database makes notification delivery a replaceable mechanism.
Make gateway acceptance specific enough to be useful
For a 4SAPI deployment, verify more than a successful greeting. Check the current 4SAPI documentation for the chosen model and Responses route, then confirm creation with background parameters, retrieval of the returned identifier, cancellation, and the preservation of terminal details. The public OpenAI-compatible base is https://4sapi.com/v1, but compatibility with chat requests alone does not establish these lifecycle capabilities. The advanced gateway integration discussion provides adjacent operational context.
Run the native reference with native credentials; use a separately configured gateway client and gateway credentials only after those routes are documented for your account. Capture sanitized request and response shapes from a small synthetic job, including the cancellation race. If a required operation is unavailable, keep that workload on a confirmed endpoint instead of disguising a synchronous request as a durable background job. You can open a 4SAPI account, inspect the available model route, and make this narrow lifecycle check your first acceptance test.




