Concepts
Workflows
A workflow is a DAG of nodes and edges, executed by DirectFlow — parallel where possible, deterministic always.
Anatomy of a definition
A definition is plain JSON validated by zod schemas from @directos/shared — the same contract the API, the editor and the SDK all use:
{
"nodes": [
{
"id": "n1",
"type": "trigger.manual",
"name": "Start",
"position": { "x": 0, "y": 0 },
"params": {}
},
{
"id": "n2",
"type": "http.request",
"name": "Fetch orders",
"position": { "x": 280, "y": 0 },
"params": { "url": "https://api.example.com/orders" },
"settings": { "retries": 2, "timeoutMs": 10000 }
}
],
"edges": [{ "id": "e1", "source": "n1", "target": "n2" }]
}
| Field | Meaning |
|---|---|
node.type | Node type id, e.g. http.request — must exist in the registry. |
node.name | Display name, unique per workflow — referenced by {{$node["Name"]}} expressions. |
node.params | Values for the node’s declared parameters; strings may contain expressions. |
node.settings | Per-node reliability: retries, retryDelayMs, timeoutMs, continueOnError. |
edge.sourceHandle | Which output handle of the source the edge listens to. Defaults to main. |
Scheduling and parallelism
A node becomes ready when all of its incoming edges are settled. Ready nodes run concurrently up to maxConcurrency — independent branches parallelize automatically, and a merge node waits for all of its inputs. There is no step list to maintain: the graph is the schedule.
Branching by output handle
Executors emit data per output handle. The condition node, for example, returns:
return { outputs: { true: item, false: undefined } };
An edge listening on a handle that produced undefined is settled-inactive; a node whose incoming edges are all inactive is skipped, and skips propagate transitively. Use null for “active, with an empty payload”. One mechanism covers if/else, switch and future multi-way routers.
Retries, timeouts, failure policy
- Retries —
retriesattempts withretryDelayMsbetween them, per node. - Timeouts —
timeoutMsholds even for executors that ignore the abort signal: the engine races the executor against it. - continueOnError — converts a failure into
{ error }data on themainhandle so downstream nodes can react instead of the run failing.
Cancellation is first-class: aborting an execution aborts in-flight nodes and marks the run canceled.
Validation
Duplicate ids or names, unknown node types, dangling edges and cycles are rejected before anything runs.
Execution events
Every run streams execution.started, node.started / finished / failed / skipped / log and execution.finished through a callback. The API forwards them to the WebSocket room execution:{id} and persists the final snapshot — this is what powers live node status in DirectStudio and the events API.