Executions & Streaming
How agent and flow runs are triggered, tracked, and streamed in real time.
Both agents and flows are run through the same execution resource — the
API doesn't distinguish between them at the transport level, only at
resource_type.
Creating an execution
POST /api/v1/executions
{
"resource_type": "agent" | "flow",
"resource_id": "<agent_or_flow_id>",
"project_id": "<project_id>",
"input": { "input": "Hello!" }
}
→ 201: { "exec_id": "...", "status": "pending" }The flow's Input step reads the user's message from input.input, input.message,
or input.user_input — whichever is present.
Streaming over SSE
GET /api/v1/executions/{exec_id}/streamThis opens a text/event-stream connection. Event types:
Event type | Emitted by | Meaning |
|---|---|---|
token | agent + flow | An incremental chunk of the model's response |
reasoning | agent + flow | An incremental chunk of the model's extended-thinking/reasoning text (opt-in, see below) |
step | agent only | An inference step (phase: "thinking", etc.) — see below |
tool_call | agent only | The model is invoking a tool (name + input) |
observation | agent only | The result returned from a tool call |
summary | agent only | The agent's Layer 1 running summary was updated this turn (see below) |
flow_step | flow only | One flow node started/finished — carries node_id, its status, and the current uo_data envelope |
done | agent + flow | The execution finished |
error | agent + flow | The execution failed; detail explains why |
A flow run traces its progress only through flow_step events: a Tool/MCP Tool
node inside a flow is reported as a flow_step, not as a tool_call/observation
pair. Those two events belong to agent executions.
The LLM flow node has a stream config toggle (default true) controlling whether its
output arrives as incremental token events or as a single chunk once the full response is
ready; the Agent flow node always streams incrementally. Nodes whose emit_to_output
is turned off are silenced for token/reasoning — their text never reaches the caller —
but their flow_step trace is still emitted, so the run stays fully observable.
Multi-turn conversations
Agent conversation state is client-driven, not persisted server-side: each
POST /executions call is stateless from the API's perspective, so the
caller resends the conversation on every turn via input.history (a flat
list of {"role": "human"|"assistant", "content": ...}).
Once a conversation grows past the agent's max_tokens_before_summary
(Layer 1 running summary, see Context Management),
the backend compacts older messages into a rolling summary and emits it as a
summary event ({"type": "summary", "content": {"summary": "...", "token_count": N}}).
Callers should capture the latest summary event and resend it as
input.summary on the next call, alongside only the most recent few
messages in input.history — otherwise the backend has no persisted
summary to build on and recomputes one from scratch from the full raw
history every time the threshold is crossed again, which works but wastes
an extra LLM call per turn.
Inference steps
UnderOcean surfaces the agent's internal thought → action → observation
loop directly to the UI as step events, rather than only showing the final
answer. This is core to the platform's "visibility into agent reasoning"
principle — users can watch an agent decide to call a tool, see the tool's
output, and see how that changes its next step, as it happens.
Reasoning / extended thinking (opt-in)
Agents, and the Agent/LLM flow nodes, support an optional reasoning_effort
setting (none | low | medium | high, default none). When enabled on
a model that supports it (e.g. Anthropic extended thinking, OpenAI/DeepSeek/
Grok reasoning models via LiteLLM), the model's reasoning/thinking text is
streamed as reasoning events alongside token events, and persisted to the
execution's reasoning field once the run completes. Models that don't
support reasoning silently ignore the setting (no error).
Checkpointing
Execution state is checkpointed as it runs, which enables:
- Resuming long-running flows after a pause (human-in-the-loop steps)
- Recovering execution state after a transient failure