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.

A three-node workflow on the Studio canvas
A workflow as you actually meet it: nodes with typed ports on the canvas. The document behind it is an implementation detail you rarely need to read.

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:

  • $json is 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 body is 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 done port. 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 ERROR port with the exception — instead of failing the run. FINALLY runs 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:

  1. 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.
  2. 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).
  3. 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.