Workflows & nodes
A workflow is a directed graph of nodes connected by edges between typed ports. You build it on the Studio canvas; the engine executes the same graph — in the editor’s test runs and, deployed, on every host.
One format, everywhere
There is exactly one workflow document format — flowdrome.workflow.v1 — used by the Studio
editor, export/import, the Nucleus registry, and every host’s embedded engine. The graph you
draw is the document; nodes carry their id, type, label, position and configuration (plus
◈ carrier attachments like a wired AI Model), edges carry source/target node and port. What you export is what deploys — there are no converters
between an “editor format” and a “runtime format”, so there is no class of bug where the two
drift apart. (The editor’s canvas library has its own in-memory representation, but it is
adapted on the fly and never persisted.)
This has practical consequences:
- An exported file is a complete, runnable artifact — share it, diff it in git, re-import it anywhere.
- The node reference can be generated from the same registry the engine uses, so the docs cannot drift from the code.
- A workflow built by hand (or by a script) deploys exactly like one built on the canvas.
Nodes and ports
Every node declares its input and output ports. Most have one of each; flow-control nodes have
more — If has True/False outputs, Switch grows one output per rule, Merge takes
Input 1 … Input N, Try exposes SUCCESS/ERROR/FINALLY. Ports are visible on the
canvas and the engine routes data per port — a branch is a wire you can see, not a
convention buried in config.
Some port sets are dynamic, derived from config: add a rule to a Switch and a new output handle appears; set Merge’s “Number of inputs” to 4 and four pins render. The reference pages note every such rule.
Port labels always render in full — a node with long port names (an HTTP Trigger with a
route table, a parallel Execute Workflow) widens to fit its longest label instead of truncating. The
label’s typography tells you its state at a glance: bold means something is wired to that
port, italic means the port is optional (the node works without it — ◈ Memory, Feedback,
Try’s Finally), and an optional port that’s wired is both.
For multi-input nodes the engine orders inputs deterministically by target port — Input 1 is always Input 1 — which is what makes choices like Merge’s “prefer Input 2 on clash” stable and meaningful.
Properties: one model, rich where it counts
Node configuration is a flat list of typed properties — string, number, boolean, select, multiselect, JSON, code, cron, rows, credential — rendered by one generic properties panel. That uniformity is deliberate: every node feels the same, and adding a node to the catalog automatically gives it a complete editing UI, validation, and a generated reference page.
Where a richer editor earns its place, it’s there: a cron builder with live next-fire preview,
CodeMirror for JavaScript, a structured rules editor for Switch, and the visual JSON editor
everywhere JSON is edited or displayed. Properties can also declare visibility rules
(“shown when mode === "cron"”) so panels stay short.
Expressions: {{ }} in any string field
Any plain string field can hold a {{ }} expression — real JavaScript evaluated per item
at the node boundary, in every node, identically in the editor and on deployed hosts:
Order {{ $json.orderId }} for {{ $json.customer }} — {{ $json.items.length }} items
Details that matter in practice:
$jsonis the incoming item. Mixed text interpolates; a field that is only an expression yields the raw value (numbers stay numbers).- Expressions descend into JSON-in-a-string: when
bodyis a JSON string (a typical HTTP response),{{ $json.body.results[0].name }}parses it on the way in instead of forcing a script node. - The run-data viewer’s right-click → Copy Flowdrome path writes the correct expression for any field you can see — the fastest way to get paths right.
- Fields with their own semantics (code editors, JSON editors, credential/secret fields) are excluded on purpose.
${credential.*}and${variable.*}placeholders resolve once at run start, before any expression runs — see Credentials & variables.
Validation before you run
Every node has a verify rule set, evaluated live in the editor: per-port connectivity (a trigger with no outgoing wire is an error), format checks (cron expressions, URLs, JSON fields), and content warnings (empty code). The badges sit bottom-right on each node — dim when clean, lit with a count when not — with hover details. The same checks gate nonsense early in CI-ish flows: a workflow that fails verification tells you where and why before any data moves.
Frames: Loop, Try and Thread Pool
Three constructs are frames — visual containers whose membership means something to the engine. You drag nodes into them; membership is containment, persisted in the document:
- Loop Over Items — members run per batch over an item list, with batch size and
concurrency settings; results collect on the frame’s
doneport. See the Loop reference for the captured frame. - Try — an error boundary. Members run normally; a member that throws is retried per the
frame’s policy and the failure routes to the frame’s
ERRORport with the exception — instead of failing the run.FINALLYruns either way. See Try and error handling. - Thread Pool — queue-fed concurrency: the frame consumes from an input queue, runs its body on an elastic worker pool, and feeds an output queue. The worker-pool pattern as a drawing.
A fourth frame — Snippet — is purely visual grouping for big graphs; the engine ignores it.
Processing 1,000 items
Three mechanisms, from cheapest to most explicit — most flows use the first two:
- Whole-set (default). List-aware nodes (Sort, Dedupe, Aggregate, Split Out, Limit, CSV …) process the entire list in one execution. One node run, no per-item overhead.
- Run once per item. A per-node toggle that maps the node over its input list. Branching nodes (If / Filter / Switch) then split the stream: a 100,000-item list through an If yields two real item streams, routed per port. Items that error can be dropped instead of failing the run (the per-item error policy).
- Loop Over Items frame — when a whole section of the graph must iterate, with explicit batching and concurrency.
Sub-workflows
The Execute Workflow node runs other stored workflows — sequentially, or in true parallel
with one labelled output per child (w0, w1, …). Compose libraries of small flows and
orchestrate them; the debugger can step into a sub-workflow run and
surface back to the parent. Workflows-as-building-blocks is also how the
AI Agent treats your flows: any stored workflow can be exposed to an agent
as a callable tool.