Skip to main content
A flow is plain JSON. It has no positions or UI types, so you can write it by hand, generate it with the TypeScript flow() builder, and keep it in git.
A flow can also carry two optional fields for the callers that use it:
string
What the flow does. MCP clients see it as the tool description.
object
JSON Schema of the run input, with an object at the top level. MCP clients see it as the tool arguments. GET /v1/flows returns it. The engine does not enforce it.

Nodes

Each node has an id, a type and a config. Optional fields apply to every node type:
"any" | "all"
default:"any"
With any, the node runs when at least one incoming edge is live. With all, every incoming edge must be live.
{ attempts, backoffMs }
Retries a failed node with exponential backoff. backoffMs defaults to 200.
number
Aborts the node through its AbortSignal after this many milliseconds.
boolean
Memoizes successful results, keyed by node type, config and upstream results. The default cache is an in-memory LRU.
unknown
Ignored by the engine. An editor can store layout here.

Edges and branching

An edge connects from to to. An edge with when is live only if the condition holds on the upstream node’s result. path reads into the result (data.choice, data.confidence, output), and op is one of eq, neq, gt, gte, lt, lte. An edge is skipped when its source failed, was skipped, or its condition is false. A node whose incoming edges are all skipped is skipped as well, and so on downstream. Set join: "all" when a node needs every input, for example when it acts only if a class matched and the action is known.

Execution

compileFlow runs once at startup. It checks node types and configs, provider capabilities, duplicate ids, unknown edge targets and cycles, then sorts nodes into levels. Runs walk the precomputed levels.
  • Nodes in one level run in parallel.
  • A failed node does not stop unrelated branches. Its downstream nodes are skipped, and ok is false in the result.
  • A run supports a timeoutMs and an AbortSignal. Both reach every fetch and provider call.
  • Nodes return results and do not throw. A thrown error is caught and recorded as a node failure.

Templates

Strings in prompt, llm, decision and http configs accept {{path}} placeholders. The scope holds the run input under input and each upstream node under its id. An unknown variable fails the node instead of rendering an empty string. When a value is exactly one placeholder, such as "{{input.devices}}" in an http body or a decision’s options, it keeps its type (array, number) instead of becoming a string.

Events

The onEvent callback receives node:start, node:done, node:error, node:skipped and provider:call. Events are plain data. The HTTP server streams them as server-sent events.