Protocols · November 7, 2026 · 7 min read

Background Responses and webhooks: finishing long agent jobs without holding the socket

How OpenAI Background mode and Standard Webhooks let agent builders run multi-minute Responses jobs without proxy timeouts, with polling, resume cursors, thin event payloads and signature verification as of November 2026.

Some links are affiliate links; we may earn a commission at no extra cost to you. As an Amazon Associate I earn from qualifying purchases. Disclosure

Agent loops that finish in a few seconds are happy to sit on one HTTP connection. Reasoning models, deep research, and multi-tool Codex-style jobs are not. They can run for minutes while a load balancer, a browser tab, or a serverless function quietly closes the socket. When that happens on a normal synchronous Responses call, the work on the provider side often dies with the connection. Background mode flips that contract: the job keeps running after you hang up, and you come back later by polling or by listening for a webhook. This post walks through how that protocol looks as of November 7, 2026, what the thin webhook payload actually contains, and how to wire it into an agent harness without inventing your own job queue semantics.

Why the open socket is a bad plan for long agent work

A synchronous Responses request is simple. You POST, you wait, you get output or a stream of events. That design assumes the client stays online for the whole generation. OpenAI's Background mode guide calls out the opposite case: agents like Codex and Deep Research show that reasoning models can take several minutes, and holding a connection through proxies and idle timeouts becomes the reliability problem. Canceling a synchronous response is literally terminating the connection. That is fine for a chat UI. It is a bad default for a worker that may restart, for a mobile client that sleeps, or for an edge function with a hard wall clock.

Background mode is the Responses API answer to that. You set "background": true on create. The API accepts the job, returns a response object that starts in queued or in_progress, and keeps generating after your HTTP client disconnects. You then either poll GET /v1/responses/{id} until the status leaves those two states, or you subscribe to project webhooks for response.completed, response.failed, response.cancelled, and response.incomplete. The important mental model is that the response id is now a durable handle, not a one-shot reply.

This is different from the Batch API. Batch is for thousands of independent calls at half price with a window measured in hours. Background is for one long interactive-quality job that still needs to finish on a human-ish timeline, without making your process babysit a socket the whole time.

Starting a background response

The create call looks like an ordinary Responses request with one extra field. The official examples use models such as GPT-6 Astra and set background to true. The first reply is not the finished novel or the finished tool loop. It is a status you can print and an id you must store. From that point the lifecycle is yours to track.

Polling is the simplest consumer. The background guide's pattern is a loop that sleeps a couple of seconds while status is queued or in_progress, then retrieves again. When the status leaves those states, treat it as terminal and read output_text or the structured output items. Canceling is a separate POST to /v1/responses/{id}/cancel. The docs note that canceling twice is idempotent: later calls just return the final response object, which matters when two workers both decide the user hit stop.

Retention is the footgun. Background requests from Zero Data Retention projects run with store=false, and response data is only kept on disk for roughly ten minutes so you can poll. On Modified Abuse Monitoring projects, background responses are retained after that window only when you explicitly pass store=true. If you omit store or set it false, the object disappears after about ten minutes. An agent orchestrator that fires a background job and plans to fetch the transcript tomorrow needs an explicit store flag and a project policy that allows it. Do not assume the default chat retention rules still apply.

Streaming without marrying the connection

You can combine background and stream. That gives you live tokens while still leaving the job alive if the stream drops. The create response emits events with a sequence_number. You keep a cursor on every non-keepalive event, and if the connection dies you resume with GET /v1/responses/{id}?stream=true&starting_after={cursor}. Keepalives deliberately use connection counters rather than stored-event resume positions, so bumping the cursor on a keepalive would skip or duplicate real events.

There are two limits worth designing around. First, time to first token on a background stream is currently higher than on a synchronous stream; the docs call that out as a known gap. Second, you can only start a new stream from a background response if you created it with stream=true in the first place. A poll-only background job cannot later be turned into a resumable stream. If your product might show partial progress in a UI, create with both flags from the start even if the first client only polls.

Webhooks: thin events, Standard Webhooks signatures

Polling every two seconds works for a single debug job. A fleet of agent runs wants a push. OpenAI's webhooks guide delivers project events to an HTTPS endpoint you register in the dashboard. Delivery follows the Standard Webhooks specification: the request carries webhook-id, webhook-timestamp, and webhook-signature headers, and you verify them with the signing secret shown once at endpoint creation (or with the official SDK webhooks.unwrap helper, or with the Standard Webhooks libraries).

The payload for a finished background job is intentionally thin. A response.completed event looks like an event object with a type, a created_at timestamp, and data: { "id": "resp_..." }. The webhook does not include the model output. Your handler verifies the signature on the raw body, returns 2xx quickly, and then fetches GET /v1/responses/{id} to load the real result. That matches the Standard Webhooks preference for small payloads and puts data access behind an authenticated API call you can audit. The webhook events reference also lists response.failed, response.cancelled, and response.incomplete, plus the parallel batch, fine-tuning, eval, realtime, and safety event families. For agent runtimes the response.* quartet is the one that closes a background job.

Delivery behavior is part of the protocol contract. OpenAI retries failed deliveries for up to 72 hours with exponential backoff. It does not follow 3xx redirects; a redirect is a failure, so the registered URL must be the final destination. Duplicates can happen, and the guide tells you to treat webhook-id as an idempotency key. That pairs cleanly with storing the response id as your job primary key: process each webhook-id once, and if you see the same response id again, short-circuit.

Signature verification belongs in front of any side effect. The SDK unwrap path needs the original bytes, which is why the sample Express and Flask servers avoid express.json() / automatic JSON parsing before verify. If the signature is wrong, return 400 and do not enqueue work. If you lose the signing secret, rotate it in the dashboard rather than disabling verification "temporarily."

A concrete agent harness shape

Put the pieces together for a coding or research agent that sometimes needs a multi-minute Responses turn with tools.

On the request path, decide up front whether the turn is short enough for a synchronous call. If the model, the tool budget, or the product SLA says the turn may exceed your proxy idle timeout, create with background: true, persist the response id next to your run id, and return "accepted" to the caller. Prefer webhooks for completion in production, and keep a slow poller as a safety net for missed deliveries. Subscribe at least to response.completed and response.failed; add cancelled and incomplete if your UI exposes cancel or partial states.

On the webhook path, verify, dedupe on webhook-id, fetch the response, and only then apply tool results to your own state machine. Remember that a background Responses job is still one model turn (or one streamed turn), not a full multi-hop agent by itself. Your harness still runs tools and may kick off the next background turn. What Background buys you is reliability on each long model step, not a replacement for your loop. For offline evals and nightly enrichment, keep using batch pricing instead; webhooks for batch.completed are available on the same endpoint machinery when you want push there too.

If you also need live tokens in a dashboard, create with background and stream, fan the first stream out to the UI, and on disconnect resume from sequence_number while the webhook remains the source of truth for "this job is done." Do not treat a dropped stream as a failed job when background mode is on.

For a systems-level treatment of long-running production LLM workflows, retries and operational feedback loops around jobs like these, AI Engineering by Chip Huyen is the book we keep pointing people at next to the vendor protocol docs.

Practical picks

Use synchronous Responses when the turn reliably finishes inside your shortest timeout and the user is staring at the stream. Use Background plus webhooks when the turn can run for minutes, when workers restart, or when you refuse to pay for an open connection you might lose. Use Batch when you have many independent calls and a hours-scale deadline. Store the response id the moment create returns, set store deliberately under ZDR and abuse-monitoring rules, verify every webhook on the raw body, and fetch the thin event's id before you trust any output. The protocol is small on purpose. The reliability comes from treating the response as a job handle instead of a single HTTP round trip.

Sources

OpenAI: Background mode, OpenAI: Webhooks, OpenAI: Webhook events reference, Standard Webhooks, Standard Webhooks specification, OpenAI: Batch API.

Newsletter

The daily agent toolkit.

New tools, MCP servers and frameworks, in one short email every morning. Free, no spam. Sign up and get the free AI agent cost cheat sheet (PDF).