The lifecycle your interface should show
| Event or state | What it establishes | What the application should do |
|---|---|---|
response.steer sent | The client asked to add input to an active response on the current WebSocket connection. | Store the target response, your change-intent ID, and the exact delta. |
response.steer.accepted | The input is queued. The server may finish the current output item or hosted tool first. | Show queued, not applied. Keep listening for the response and steer events. |
response.steer.pending | The model needs application-managed input, such as a function result or approval, before it can continue. | Resolve that requirement, then send response.create with the required input and the correct previous_response_id. |
response.steer.failed | The queued change failed. The event is tied to the steer ID. | Keep the old and new requirements visible, record the error, and decide whether a fresh response is safer than replay. |
| response completes | The active response ended; it does not by itself prove every requested change was followed. | Compare the output and side effects with the revised acceptance checks. |
Queued steering exists on the current WebSocket connection and is not stored with the original response. If the connection drops, reconcile the response and tool state before deciding whether to send the change again. Blind replay can duplicate work.
Describe the change as a small delta
Imagine an agent has finished a report and is preparing to email it when the requirement changes: the report should be saved for legal review instead. A compact delta is easier to apply than a rewritten project brief:
PRESERVE: The completed report and its source notes.
STOP: Do not send email or open a compose window.
CHANGE: Replace external delivery with a local review draft.
NEXT: Save the draft in reviews/vendor-north/.
NEED: Report whether any message or external job had already started.
PRESERVE prevents needless rework. STOP governs future action. CHANGE makes the old and new requirements auditable. NEXT gives the model a safe continuation. NEED surfaces consequences the model cannot infer.
Implementation flow
- Confirm that the response is still active and that steering is still appropriate. A new outcome, source boundary, or authority model often deserves a fresh response.
- Inventory output already shown and every hosted tool, custom function, or external job already queued or started.
- Send
response.steeron the same WebSocket connection with the active response'sprevious_response_idand the delta as input. - Record the returned steer ID. Treat
response.steer.acceptedas an acknowledgement, not a completion receipt. - If
response.steer.pendingarrives, supply the requested function result or approval through a newresponse.createcontinuation. This is the case where another create event is expected. - At the end, reconcile output, tools, approvals, and external side effects against the revised brief.
Minimal steering event
{
"type": "response.steer",
"previous_response_id": "resp_123",
"input": [{
"role": "user",
"content": [{
"type": "input_text",
"text": "PRESERVE: the report. STOP: external delivery. CHANGE: save a local review draft. NEXT: write the draft. NEED: disclose any action already started."
}]
}]
}
The exact client wrapper can differ. Keep protocol details close to the current official documentation and test the event sequence in your own SDK and application.
Handle the three races explicitly
| Race | Typical failure | Useful safeguard |
|---|---|---|
| Text | Old wording has already streamed to the user. | Preserve it in the audit trail and issue a corrected final handoff; steering cannot retract displayed text. |
| Tool | A hosted tool finishes, or an application job keeps running, before the change applies. | Track tool state separately with an idempotency key and real statuses such as started, cancellation requested, canceled, completed, and verified. |
| Approval | An approval remains attached to a target that has changed. | Invalidate approval when recipient, target, scope, cost, or irreversible effect changes; ask again with the new target visible. |
Test outcomes, not the presence of a steering button
- Before the current item ends: confirm the change is queued and earlier text remains visible.
- During a hosted tool: confirm the tool may finish before the new instruction is used.
- During an application function: provoke
response.steer.pending, return the required result, and verify the continuation. - Failure: verify
response.steer.failedis associated with the correct steer ID and does not silently trigger a replay. - Disconnect: reconnect, read actual response and job state, and prevent duplicate side effects.
- Completion: check that preserved work stayed intact, the obsolete action did not begin, and the revised deliverable exists.
Evidence behind this guide
The protocol description was checked against OpenAI's steering documentation on 2026-09-11. A separate response-only fixture showed that Astra, Sol, and Terra could all follow the preserve/stop/change/next wording when an email step became a local draft. That fixture did not open a WebSocket, emit live steer events, run tools, or test disconnect recovery. Use the downloadable checklist as an implementation aid, then retain your own event log and side-effect evidence.
