stream: true works on /v1/messages, /v1/chat/completions and /v1/responses with and without a server prompt. The format depends on the API: Anthropic SSE, OpenAI Chat chunks or Responses events respectively.
stream: trueSSEcontent_block_deltamessage_stopchat.completion.chunkdata: [DONE]response.output_text.deltaresponse.completed| Endpoint | Terminal event | Notes |
|---|---|---|
/v1/messages without a server prompt | message_stop | Native text/tool/thinking/signature/citation deltas |
/v1/messages with an applied prompt | message_stop | Text and client tools; no signed thinking or citation deltas |
/v1/chat/completions | data: [DONE] | OpenAI-compatible chunks |
/v1/responses | response.completed | Responses events; at the output limit the final object has status: incomplete |
curl -N https://api.guardrelay.ai/v1/messages \
-H "x-api-key: $GUARD_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 256,
"stream": true,
"messages": [{"role": "user", "content": "Write a haiku about the sea"}]
}'Claude Messages event types without an applied server prompt:
message_start— start of the response, metadata.content_block_start/content_block_delta/content_block_stop— content chunks.message_delta— top-level changes (e.g. stop_reason).message_stop— end of the response.
Without an applied server prompt, content_block_delta may also contain thinking_delta, signature_delta, input_json_delta, and citations_delta. Ignore unknown event types safely — Anthropic may add more.
Responses SSE uses stream: true at /v1/responses and /codex/v1/responses. Native WebSocket uses wss://api.guardrelay.ai/v1/responses or the retained wss://api.guardrelay.ai/codex/v1/responses: the client sends response.create events and receives Responses events. Chat Completions and Messages support SSE, but are not renamed into Responses events.
Kimi streams on all three surfaces; the plain Chat Completions frames and the repeated usage are covered on Kimi: streaming and usage. Composer and Grok stream on all three surfaces, each in its native format.
Pass stream: true and the answer arrives as SSE frames in the ordinary OpenAI shape. Every frame is a chat.completion.chunk, the piece of text sits in choices[0].delta.content, and the line data: [DONE] closes the stream.
curl -N -X POST https://api.guardrelay.ai/v1/chat/completions \
-H "Authorization: Bearer gd-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "k3",
"messages": [{"role": "user", "content": "Write a haiku about the sea"}],
"max_tokens": 256,
"stream": true
}'Kimi puts the final statistics into the closing frame next to finish_reason, even when stream_options.include_usage was not asked for. With include_usage: true the same statistics arrive once more as a separate frame with an empty choices. It is the same number — do not add them up.
| Endpoint | Composer and Grok events |
|---|---|
/v1/messages | message_start, content_block_start, content_block_delta, content_block_stop, message_delta with the final usage, message_stop |
/v1/chat/completions | chat.completion.chunk frames, the last one carrying finish_reason: "stop" and usage, then data: [DONE] |
/v1/responses | response.created, response.output_text.delta, response.output_text.done, response.completed with usage |
curl -N -X POST https://api.guardrelay.ai/v1/chat/completions \
-H "Authorization: Bearer gd-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "cursor-grok-4.6-low",
"messages": [{"role": "user", "content": "Write a haiku about the sea"}],
"max_tokens": 256,
"stream": true
}'curl -N -X POST https://api.guardrelay.ai/v1/chat/completions \
-H "Authorization: Bearer gd-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "composer-2.5",
"messages": [{"role": "user", "content": "Write a haiku about the sea"}],
"max_tokens": 256,
"stream": true
}'Add stream: true to the same body. Events remain native to the selected API: Guard does not translate Messages, Chat, and Responses into one another.
curl -N https://api.guardrelay.ai/glm/v1/responses \
-H "Authorization: Bearer $GUARD_API_KEY" \
-H "content-type: application/json" \
-d '{"model":"glm-5.3","input":"Explain 2+2 briefly","max_output_tokens":4096,"store":false,"stream":true}'| API | Text | Successful terminal | Capped terminal |
|---|---|---|---|
| Messages | content_block_delta | message_delta + message_stop | stop_reason: max_tokens |
| Chat | choices[].delta.content | finish_reason + [DONE] | finish_reason: length |
| Responses | response.output_text.delta | response.completed | response.incomplete + max_output_tokens |
response.incomplete is a terminal event, not a transport error. Preserve delivered output and inspect incomplete_details.reason.
If the client cancels before exact terminal usage is available, Guard releases the reserve without a guessed debit; do not infer that provider usage was zero.
SSE streaming works in all three APIs: send stream: true. Frames follow the selected API: Anthropic events in Messages, chat.completion.chunk and data: [DONE] in Chat Completions, response.* events in Responses. Reasoning, when its display is requested, comes in separate frames: thinking_delta in Messages and response.reasoning_summary_text.delta in Responses. Chat Completions has no way to request the reasoning display.
SSE streaming works in all three APIs: send stream: true. Frames follow the selected API: Anthropic events in Messages, chat.completion.chunk and data: [DONE] in Chat Completions, response.* events in Responses.