Step-Through Execution API
Drive a flow one step at a time. Inspect outputs, edit them, and continue — server stays stateless between calls.
Base URL: https://api.noukai.xyz/api/v1/seq
The step-through endpoint runs a published flow one step at a time.
Between calls the server holds no state — the client carries the
cursor (executionId, stepIndex) and the accumulatedOutputs map. On
each call you can inject inputOverrides (replace a prior step's
output) or blockOverrides (edit a block's prompt/model before it
runs).
Use it when you need:
- A human-in-the-loop UI that shows each step's output before continuing.
- "Edit and re-run from step N" flows.
- Debugging or replaying flows with surgical input changes.
For straight end-to-end execution use /execute instead.
Endpoint
The versioned form pins to a specific published version (recommended —
stable tree across calls). The unversioned form uses the production
version. Draft (v0) is not allowed: the tree must be stable for
the cursor to be meaningful.
Path Parameters
| Parameter | Description |
|---|---|
org | Organization slug |
project | Project slug |
flow | Flow slug |
version | (optional) Published version number, e.g. v2 |
Authentication
Same auth as /execute — API keys or JWT both accepted.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
executionId | string | null | No | null on the first call (server creates one and returns it in run_started). Required on every subsequent call. |
stepIndex | integer | Yes | 0-based cursor — which step to run next. |
accumulatedOutputs | object | When stepIndex > 0 | Map of stepId → output from prior steps. The client owns this. |
message | string | When stepIndex == 0 | Initial input (same channel as /execute). Ignored on later calls. |
parameters | object | No | Extra initial inputs. First call only. |
inputOverrides | object | No | Replacement values for prior outputs (data plane). Merged on top of accumulatedOutputs before deriving pipeline_input. Ignored at stepIndex == 0. |
blockOverrides | object | No | Per-step config edits (config plane) — prompt, model, inputSchema, outputSchema, etc. Re-applied every call. |
runRemaining | boolean | No | If true, run from stepIndex to the end without pausing. Streams step_progress between steps and ends with run_completed. |
tools | array | No | Tool definitions for tool-calling blocks. Same shape as on /execute. See Tool Calls on /step. |
toolChoice | string | object | No | "auto" | "none" | "required" | {"type":"function","function":{"name":"x"}} |
toolCallMessages | array | When resuming a tool pause | Full conversation carrying assistant tool_calls and the caller's role:"tool" results. |
iterationsUsed | integer | When resuming a tool pause | Cumulative tool-call round-trips for the paused step. Echo back from the prior step_paused_for_tool_calls event. |
Two override planes. inputOverrides rewrites the data flowing
between steps (e.g. "pretend block A produced this instead"). blockOverrides
rewrites the block configuration before execution (e.g. "use this
prompt for block B"). They are independent — you can use both in one call.
Response
Content-Type: text/event-stream
The endpoint streams Server-Sent Events. Event names and payload shapes:
run_started (first call only)
Save executionId — you must echo it back on every subsequent call.
block_started
block_completed
Merge output into your local accumulatedOutputs keyed by stepId.
step_paused — single-step mode, more steps remain
Bump your local cursor to nextStepIndex and (optionally) wait for user input before the next call.
step_progress — runRemaining: true only
step_paused_for_tool_calls — block paused for tool calls
Emitted instead of block_completed when the running block's model returns tool calls. The stream ends after this event; the run stays in_progress server-side until the client resumes.
| Field | Description |
|---|---|
runId / executionId | Same value; both emitted for client back-compat. |
stepId, stepIndex | The paused block and its index in the flattened step plan. |
iterationsUsed | Cumulative tool-call round-trips for this block. Echo back on resume. |
toolCallMessages | Full conversation up to the pause. Append role:"tool" results and echo back. |
toolCalls | Convenience: the last assistant turn's tool_calls. Same IDs your resume call must answer. |
accumulatedOutputs | Outputs of blocks completed before this pause. Echo back. |
run_completed
Or, on failure:
Tool Calls
/step supports the same tool-calling loop as /execute, delivered over SSE: when the running block emits tool calls, the stream ends with step_paused_for_tool_calls and the client carries the conversation back on the next call.
Block prerequisites
The block must have processor_config.tools_enabled: true in the published flow version. Tool-calling blocks cannot live inside a parallel step or a loop in v1 (TOOLS_IN_NON_SEQUENTIAL_STEP).
Round-trip
- Send a fresh
/stepcall withtools(and optionaltoolChoice) for the tool-calling block. - If the model emits tool calls, the stream ends with
step_paused_for_tool_calls. The run staysin_progress. - Execute the calls in your process. Append each result as
{"role":"tool","tool_call_id":"...","content":"..."}totoolCallMessages. - POST
/stepagain with the samestepIndex(do not advance) and:executionIdfrom the pause event,toolCallMessages(extended with your tool results),iterationsUsedechoed from the pause event,toolsre-sent unchanged so the model can call them again.
- The block re-enters its tool loop. It may pause again (multi-step tool use) or complete with
block_completed; the run then continues to the next step normally.
Pause-on-resume IDs must match
Every id in the prior assistant tool_calls needs a matching role:"tool" message with that tool_call_id. Mismatches return 400 TOOL_RESULTS_MISMATCH before the stream starts.
tools / toolChoice are per-request
The server keeps no state between calls. Re-send tools (unchanged) and toolChoice on every call — fresh or resume — that should run a tool-enabled block. Omitting them means the next turn runs without tools available.
tools sent on a step whose block does not have tools_enabled: true is silently ignored for that step. 422 TOOLS_NOT_ENABLED only fires when no block in the flow has the flag on.
Limits
Same as /execute: 64 tools per request, 16 KB parameters schema, 256 KB per tool result content, 1 MB total toolCallMessages payload (returns 413 MESSAGES_TOO_LARGE), 25 iterations default per block (409 TOOL_ITERATION_LIMIT on overflow; override via the block's processor_config.max_tool_iterations).
Pipeline-Input Derivation
The server picks the next step's pipeline_input from the prior step's
output (with overrides on top):
This matches /execute semantics exactly, plus the inputOverrides injection.
Loops Are Atomic
A loop step runs all its iterations in one call — you cannot pause
mid-iteration. The cursor pauses between loop steps and other steps,
not inside a loop. Same constraint as the editor's test step-through.
Error Codes
All errors are HTTP errors (not SSE events) returned before the stream starts.
| Status | code | When |
|---|---|---|
| 400 | MISSING_MESSAGE | stepIndex == 0 and no message |
| 400 | INVALID_STEP_INDEX | stepIndex is out of range |
| 400 | INVALID_TREE | Flow's stored tree fails validation |
| 400 | INVALID_VERSION | Tried to use v0 (draft) |
| 400 | NO_STEPS | Flow has no steps tree (passthrough flow) |
| 400 | STALE_TREE | A stepId in accumulatedOutputs / inputOverrides no longer exists in the published tree (the flow was re-published mid-session). The body includes the new plan so the client can reset. |
| 400 | TOOLS_INVALID | Bad tool shape, duplicate name, oversized parameters, or oversized tool result content. |
| 400 | TOOL_RESULTS_MISMATCH | Tool result IDs don't match the prior assistant tool_calls. |
| 401 | — | Missing or invalid auth |
| 402 | INSUFFICIENT_CREDITS | Org balance below threshold |
| 404 | FLOW_NOT_FOUND | Bad slug, or flow not published |
| 404 | RUN_NOT_FOUND | executionId does not match any FlowRun |
| 409 | TOOL_ITERATION_LIMIT | Cumulative iterations reached the block's max_tool_iterations cap. |
| 413 | MESSAGES_TOO_LARGE | toolCallMessages payload exceeds 1 MB. |
| 422 | TOOLS_NOT_ENABLED | The flow has no block with tools_enabled: true. |
| 422 | TOOLS_IN_NON_SEQUENTIAL_STEP | The tools-enabled block sits inside a parallel or loop step. |
STALE_TREE is the one error worth handling explicitly — it tells you
the flow was re-published while your session was running. Recover by
showing detail.steps to the user and starting a fresh session.
cURL Example
First call (start the run):
Second call (advance cursor, carry outputs):
With an input edit (block A's output replaced before block B runs):
With tool calls — fresh call (block at stepIndex=1 has tools_enabled):
If the stream ends with step_paused_for_tool_calls, resume with the same stepIndex and the tool results appended to toolCallMessages:
See Step-Through Execution Guide for full client implementations in TypeScript and Python.