The Responses API
/v1/responses is OpenAI’s Responses API on the same engine path as chat
completions (GitHub #282): the wire Codex CLI, the OpenAI SDKs’
responses.create / responses.connect and the Agents SDK speak.
- Over HTTP,
POST /v1/responsesanswers the response object, or withstream: truethe standard server-sent events (response.created,response.output_item.added,response.output_text.delta, …,response.completed), each with asequence_number. The thinking comes back as areasoningitem (response.reasoning_text.*when streaming), the answer as amessageitem, each tool call as afunction_callitem. - Requests carry
input(a string, ormessage,function_call,function_call_outputandreasoningitems),instructions, functiontools,tool_choice,parallel_tool_calls,max_output_tokens,temperature,top_p,reasoning.effortand the ignis sampling and thinking extensions. A conversation renders the same prompt tokens as the same conversation through chat completions, so prompt reuse hits across both endpoints. - What it cannot do is refused, with OpenAI’s error and a
param, never ignored where ignoring it would change the answer: tools that are not functions,text.formatother thantext(no structured output),background: true, and an input item of another type.store,metadata,prompt_cache_key,service_tierand the like are accepted and change nothing;include: ["reasoning.encrypted_content"]adds the reasoning as the model saw it (Chat completions). ignis stores no response, soprevious_response_idon HTTP is a400 previous_response_not_found. - A length stop ends
incomplete(max_output_tokens), an engine errorfailed;usagecarriesinput_tokens_details.cached_tokensandoutput_tokens_details.reasoning_tokens.
WebSocket mode. GET /v1/responses upgrades to a WebSocket, keyed like
HTTP: Authorization: Bearer <key> on the upgrade, or a
Sec-WebSocket-Protocol entry openai-insecure-api-key.<key> (never echoed,
never logged). The client sends response.create events carrying the HTTP
body’s fields; the server answers with the same events as the HTTP stream,
one per text frame. On the socket:
previous_response_idcontinues from the connection’s latest response on that stream with only the new items, so an agent loop does not re-send its whole history on every tool call. An id the connection does not hold isprevious_response_not_found, and the client resends the full input.generate: falseis a real prewarm: the prompt is prefilled, nothing is generated, and the retained state an ordinary request would publish is kept, so the first real turn starts from it.- Streams: a
stream_id(1–256 characters of[A-Za-z0-9_.-], up to 32 per connection) runs requests in order within a stream and concurrently across streams, up to 16 active per connection; events of a named stream carry itsstream_id. Aprevious_response_idfrom another stream on a newstream_idforks a conversation. - A full engine queues instead of refusing: the request gets
response.queuedand waits in one server-wide first-in first-out queue shared by every connection, where HTTP would answer503. A queued request’s deadline,--server-stream-timeout, counts from its admission, not from its wait (deadlines). response.cancel({type, response_id?}, an ignis extension in the shape of OpenAI’s Realtime API) cancels one running or queued response; closing the socket cancels everything it had running or queued.response.steerissteering_not_supported. A bad frame is anerrorevent and the socket stays open; a connection has no time limit.- An idle socket is closed. A socket with nothing running or queued on it
and no text or binary frame from its client (a ping does not count) for
--server-socket-idle-closeseconds (file keyserver.socket_idle_close, default300) is closed by the server with a normal close frame (1000) whose reason says so. The client’s next request opens a new socket; aprevious_response_idfrom the old one isprevious_response_not_foundthere, so it sends the full input.0never closes. The value is patchable live and reaches the sockets already open within a second.
The Playground’s chat talks to the server over this socket, so its parallel
agents are not held to the browser’s six connections per host. It opens the
socket for the turn that needs it and opens another after any close; while
its tab is hidden it closes a socket no turn is using after a minute. The open
sockets and the queued socket requests are gauges on /metrics.
