Skip to content

WebSocket turns and continuation

Open a WebSocket through the same configured client used for HTTP:

from openresponses import CreateResponseRequest, OpenResponses

with OpenResponses() as client, client.websocket() as websocket:
    turn = websocket.create(CreateResponseRequest(model="my-model", input="Hello"))
    for event in turn:
        print(event.type)

    print(turn.error)        # a typed WebSocket error event, or None
    print(turn.final_response)

with client.websocket() as websocket: is the complete connection lifecycle: entering connects and exiting closes. The WebSocket URL is derived from the HTTP base URL (http→ws, https→wss) with /responses appended. Configured API-key authentication and default headers are sent on the upgrade.

websocket.create(...) accepts an ergonomic CreateResponseRequest and validates it as the WebSocket-specific WebSocketResponseCreateRequest. It adds type: "response.create" and rejects stream, stream_options, or background, including explicit None or False values. WebSocket turns are already incremental and do not use HTTP streaming or background response modes.

The async form is:

from openresponses import AsyncOpenResponses, CreateResponseRequest

async with AsyncOpenResponses() as client, client.websocket() as websocket:
    turn = await websocket.create(CreateResponseRequest(model="my-model", input="Hello"))
    async for event in turn:
        print(event.type)

Sequential turns

sequenceDiagram
    participant A as Application
    participant W as WebSocket
    participant P as Provider
    A->>W: response.create
    W->>P: one in-flight turn
    P-->>W: response.created and deltas
    P-->>W: terminal response or error
    W-->>A: typed turn result
    A->>W: next response.create

A connection permits one in-flight turn. Calling create again before the first turn reaches a terminal response or typed error raises WebSocketError. After iteration ends, inspect turn.error and turn.final_response, then create the next turn:

next_turn = websocket.create(CreateResponseRequest(
    model="my-model",
    input="Now make it shorter.",
    previous_response_id=turn.final_response.id if turn.final_response else None,
))

Text and binary frames are decoded as UTF-8. Malformed JSON, invalid known events, and invalid error envelopes raise WebSocketError or the corresponding protocol/validation error. A connection close before terminal completion raises WebSocketError; the exception does not cause an automatic reconnect. A typed error envelope has turn.error.status and nested turn.error.error.code / turn.error.error.message.

Stored continuation

When a provider stores responses, continue with the terminal response ID:

request = CreateResponseRequest(
    model="my-model",
    input="Continue from the previous answer.",
    previous_response_id=turn.final_response.id,
)

The server can reject an unavailable or evicted prior response with a typed error such as previous_response_not_found. Read turn.error.error.code and decide how to recover.

store=False continuation

With store=False, previous-response state may exist only in the active connection's cache. Reusing its ID on the same connection can work; the same ID on a new connection can return previous_response_not_found. The SDK deliberately never reconnects or resends a turn automatically because replay can duplicate model work and billable execution.

If a connection is lost, explicitly choose a recovery strategy:

  • resend sufficient explicit input on a new connection;
  • request or retain enough context to reconstruct a safe request;
  • compact the conversation and start a new chain;
  • use a stored previous_response_id only if the provider confirms it remains available.

Do not blindly retry the original frame after a disconnect.

Compaction handoff

Compaction is an HTTP operation and is not a WebSocket stream:

compacted = client.responses.compact({
    "model": "my-model",
    "input": conversation,
    "previous_response_id": prior_id,
})
new_chain = websocket.create(CreateResponseRequest(
    model="my-model",
    input=[item.model_dump(mode="json", by_alias=True) for item in compacted.output],
))

A compacted response starts a new chain; do not assume its ID is a continuation target. Compaction output is protocol input, not an instruction to execute tools.

Turn and connection errors

WebSocketError includes the protocol status and code when available. A typed error event is a normal end for that turn: inspect it and decide whether to send another turn. Malformed frames and pre-terminal closes are exceptional. There is no hidden retry/reconnect loop, so application policy remains explicit and can account for duplicate execution.