API & Event Protocol
Full interactive API documentation is served live by your own instance at
http://localhost:8000/docs — that's the authoritative, always-current
reference for request/response shapes. This page covers the one thing that
doesn't show up well in an OpenAPI viewer: the WebSocket event protocol.
One request path, two transports
POST /api/chat and WS /ws/chat both call the same underlying
orchestrator — the transport only translates internal events into frames.
Anything true of one is true of the other; the WebSocket just makes each
step visible as it happens instead of returning once at the end.
Event frames
| Frame | What it carries |
|---|---|
session | Session identity, sent once at connection start. |
status | High-level state changes. |
step_start / step_end | Bounds around one sub-task. |
reasoning_delta | Streamed manager "thinking," if the model exposes it. |
plan_delta | Streamed plan content — split from reasoning by tracking the reasoning-tag boundary as it streams in, so the UI can switch from a thinking panel to a plan view at the right moment. |
content_delta | Streamed final-answer text. |
code | Generated code for the current step. |
stdout | Real execution output. |
artifact | A file produced by execution (a chart, an export). |
approval_required | The turn is paused, not ended — carries an id you reply to. This is the one frame that distinguishes "waiting for you" from "done." |
warning / error | Non-fatal and fatal problems respectively. |
final | The synthesized answer, plus any trust-layer annotations. |
Investigation frames (the agentic loop made visible)
| Frame | What it carries |
|---|---|
iteration_start | A new loop iteration has begun. |
action | Which action the manager chose (code/consult/reflect/parallel/inspect/answer). |
observation | The result of the most recently opened action — closes the most recent one without an observation yet, rather than being matched by an id. |
finding | Something the agent learned worth surfacing directly. |
plan_revised | The plan changed mid-run in response to real output. |
assumption | A silent decision in the generated code (dropped nulls, an inner join, etc.), surfaced alongside the answer. |
verification | The result of re-deriving the headline number by a different route. |
skill | Names which installed skill informed this turn, if any. |
skill_candidate | An offer to save a recurring analysis as a skill — nothing is written unless you confirm. |
usage | What the turn cost, emitted only when a cloud model actually ran. Under local-only this frame never appears — there's nothing to meter. |
A client that ignores the newer frames still works
Every frame listed above is additive. A client built against an earlier
version of the protocol that only understands content_delta/stdout/
final degrades gracefully rather than breaking — it just doesn't render
the richer investigation view.
Subagent branches
When a step fans out into parallel sub-investigations, each branch's own
activity reuses the same frame types (action, observation, code,
stdout, …), just additionally tagged with a branch identifier. There's
no separate frame vocabulary for subagent activity to learn.