Responses API streaming
Set "stream": true on POST /v1/responses to receive Server-Sent
Events. Kindo emits the standard OpenAI Responses stream — your
existing parser works without changes.
Request
Section titled “Request”curl -N https://api.kindo.ai/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $KINDO_API_KEY" \ -d '{ "model": "claude-sonnet-4-5-20250929", "stream": true, "input": "Write a haiku about observability." }'stream is consumed by the Kindo route handler to flip the response
into Content-Type: text/event-stream mode. It is not forwarded as
a model parameter.
Event types
Section titled “Event types”Each event has a named event: line and a JSON data: payload. The
event names match OpenAI’s Responses streaming spec:
| Event | Meaning |
|---|---|
response.created | Initial response object is allocated. |
response.in_progress | Generation has started. |
response.output_item.added | A new output item (message, function call, etc.) has begun. |
response.output_item.done | An output item has completed. |
response.content_part.added | A new content part within a message has begun. |
response.content_part.done | A content part has completed. |
response.output_text.delta | Incremental text chunk for an output_text content part. |
response.output_text.done | The full text for a content part is now stable. |
response.function_call_arguments.delta | Incremental JSON for a function-call’s arguments. |
response.function_call_arguments.done | Function-call arguments are complete. |
response.completed | The whole response is finished. |
response.failed | The response terminated in an error state. |
error | Mid-stream error. See “Mid-stream errors” below. |
When the response produces reasoning or server-side tool activity (Kindo-hosted tools, MCP tools, agent runs), the same protocol carries those items too:
| Event | Meaning |
|---|---|
response.reasoning_summary_part.added / .done | A reasoning summary part within a reasoning item. |
response.reasoning_summary_text.delta / .done | Incremental reasoning-summary text. |
response.mcp_call_arguments.delta / .done | Incremental JSON for an mcp_call item’s arguments. |
response.kindo_call_arguments.delta / .done | Incremental JSON for a kindo_call item’s arguments. |
The corresponding output items (reasoning, mcp_call, kindo_call,
shell_call, web_search_call) open and close with the same
response.output_item.added / response.output_item.done pairs as
messages and function calls. Agent-run invocations
(model: "agent/<agent-id>") stream this full vocabulary live as the
run executes — see
Invoke Kindo agents.
Example stream
Section titled “Example stream”event: response.createddata: {"id":"resp_abc123","status":"in_progress","object":"response"}
event: response.output_text.deltadata: {"item_id":"msg_xyz","delta":"Logs flow through"}
event: response.output_text.deltadata: {"item_id":"msg_xyz","delta":" the gate;\nMetrics rise like steam"}
event: response.output_text.donedata: {"item_id":"msg_xyz","text":"Logs flow through the gate;\nMetrics rise like steam,\ntraces find their way."}
event: response.completeddata: {"id":"resp_abc123","status":"completed","object":"response"}SDK consumption
Section titled “SDK consumption”Standard OpenAI SDK stream=True works as expected:
import osfrom openai import OpenAI
client = OpenAI(base_url="https://api.kindo.ai/v1", api_key=os.environ["KINDO_API_KEY"])
stream = client.responses.create( model="claude-sonnet-4-5-20250929", input="Write a haiku about observability.", stream=True,)
for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True)import OpenAI from 'openai';
const client = new OpenAI({ baseURL: 'https://api.kindo.ai/v1', apiKey: process.env.KINDO_API_KEY});
const stream = await client.responses.create({ model: 'claude-sonnet-4-5-20250929', input: 'Write a haiku about observability.', stream: true});
for await (const event of stream) { if (event.type === 'response.output_text.delta') { process.stdout.write(event.delta); }}Pre-stream errors
Section titled “Pre-stream errors”If the upstream request fails before streaming begins (for example, auth fails, the model isn’t found, the body is invalid), Kindo returns a normal HTTP error response with a JSON envelope — no SSE switch. See Errors for shapes.
Mid-stream errors
Section titled “Mid-stream errors”Once the first SSE byte has been written, the outer HTTP status is
already committed at 200. Errors that arise after that point arrive
as an error event followed by the appropriate terminal event:
event: errordata: {"error":{"message":"upstream stream interrupted","type":"server_error","code":"stream_error"}}
event: response.faileddata: {"id":"resp_abc123","status":"failed","object":"response"}Treat any error SSE event as terminal — the response will not
resume.
Resuming a dropped stream
Section titled “Resuming a dropped stream”If the SSE connection drops mid-stream (a flaky network, a proxy
timeout), you can reconnect to the same response and replay the
events you missed instead of issuing a new POST — which would start a
new response. Every streamed event carries its sequence_number both in
the data: payload and as the SSE id: line:
id: 4event: response.output_text.deltadata: {"item_id":"msg_xyz","delta":" the gate","sequence_number":4}Reconnect with GET on the response id, passing stream=true and the
last sequence_number you received as starting_after:
curl -N "https://api.kindo.ai/v1/responses/resp_abc123?stream=true&starting_after=4" \ -H "Authorization: Bearer $KINDO_API_KEY"Kindo replays every event after sequence_number 4 with continuous
numbering, then continues live until the response completes (or emits
response.completed immediately if it already finished). No new response
is created. Omit starting_after to replay from the beginning. A negative
or non-integer starting_after returns HTTP 400.
Browser EventSource clients resume automatically: the runtime resends
the last id: it saw as the Last-Event-ID request header, which Kindo
honors when starting_after is absent.
The resume window is time-bounded (the same window over which a
response is retained for polling). After it elapses, a streamed resume
falls back to the response’s final items followed by response.completed
— the per-token deltas are no longer available. Retrieve the final object
with a plain GET (no stream) instead.
Agent-run responses replay from the start only
Section titled “Agent-run responses replay from the start only”Responses backed by an agent run (ids of the form resp_run_<runId>)
support full replay only — not incremental resume. Reconnect with
stream=true and starting_after=0 (or omit starting_after) to replay
the run’s events from the beginning and continue live:
curl -N "https://api.kindo.ai/v1/responses/resp_run_abc123?stream=true&starting_after=0" \ -H "Authorization: Bearer $KINDO_API_KEY"A positive starting_after on a resp_run_* id returns HTTP 400. An
agent run streams many steps, and a step that is still live emits per-token
deltas while a step already finished replays only its completed items, so the
event numbering is not stable enough to splice a partial reconnect onto
reliably. Replaying from the start avoids any gap or duplication.
See also
Section titled “See also”- Quickstart — non-streaming round trip.
- Request shape — every field Kindo honors.
- Tool use — streaming tool-call events.
- Errors — pre-stream error envelopes.