MINARA

Chat — trigger a streaming turn

POST /v1/chat/stream — Starts an agent turn and returns immediately with the session id. The turn runs in the background; its events (LLM delta

POST /v1/chat/stream

Starts an agent turn and returns immediately with the session id. The turn runs in the background; its events (LLM delta, tool call, tool result, final assistant message) stream over the multiplexed WebSocket at /v1/stream on the chat channel keyed by the returned session_id. A reconnect resumes from the per-session sequence buffer.

MethodPOST
Path/v1/chat/stream
AuthAuthorization: Bearer <token> required when GATEWAY_AUTH_TOKEN is set
Categorychat

Request body

{ "message": "analyze BTC momentum", "session_id": "chat_...", "attachments": [ { "key": "file_ab12", "filename": "chart.png", "media_type": "image/png", "size": 48213, "kind": "image" } ], "model": "claude-sonnet-4-5", "reasoning_effort": "high" }

Response body

{ "session_id": "chat_...", "kind": "chat", "is_new": true }

Notes

Every field except message is optional. message may be omitted only when resume: true continues a settled provider connectivity failure. session_id continues an existing conversation; omit it to start a new session (session_kind then picks the surface, default "chat"). retry: true replaces the session's latest turn instead of stacking a duplicate. resume: true requires an existing session_id, keeps completed tool results, and continues from the last durable boundary without adding a visible user row.

Attachments — two-step flow: upload bytes via POST /v1/files (multipart) to get a key, then reference it here. Each entry is { key, filename, media_type, size, kind } with kind one of "image" | "pdf" | "spreadsheet" | "text" | "office" (generic "file" is rejected — not readable by the agent yet). Limits: at most 8 attachments per turn, 5 MiB per image, 20 MiB per other file. Voice input rides a parallel field: voice_input_key carries the FileStore key of the original mic recording (uploaded via POST /v1/voice/transcribe?persist) so history can replay the audio; the transcript itself goes in message.

Per-turn model overridemodel runs THIS turn on the given model id without touching the deployment default (PUT /v1/llm/default-model), so concurrent clients don't affect each other. The id must be served by the active provider connection (see GET /v1/llm/available-models); a model that would require switching the connection is rejected with 400. reasoning_effort overrides the thinking tier for this turn and must be a tier the effective model supports. Bad values 400 before the turn starts.

Mid-turn steering: a user_interjection event is emitted on the WS chat channel when a message queued via POST /v1/chat/interject is injected into the running turn. The final done event carries two optional fields: interrupted: true when the turn was stopped via POST /v1/chat/interrupt, and pending_interjections: string[] listing interjected messages that arrived too late to inject; clients re-send those as normal messages. Prompt variant selection for headless callers: the optional body fields surface ("web" | "cli", default "web") and prompt_mode ("full" | "minimal", default "full") tune the system prompt — cli omits the custom-URI wire protocol, minimal mounts the lean skeleton (identity + skill/tool mechanics + base markdown) and strips personalization from the injected context while keeping the skill catalog, date, and working directory. The WebSocket wire protocol (channels, subscribe/unsubscribe frames, seq, replay, control frames) is documented on the Streaming — the multiplexed WebSocket page.

On this page