flow_data is the JSON document that defines a SuperFlow workflow graph. The workflow endpoints accept it in three places: Create Workflow, Update Workflow, and inline in Execute Workflow. This page describes the envelope, the node object, connections, and the expression syntax. The full catalog of node types lives in the node types reference.
If you build workflows in the Studio editor, you rarely need this page. It exists for callers who construct or modify
flow_data programmatically. For a task-oriented walkthrough of the same concepts in the UI, see the SuperFlow node reference.Envelope
flow_data is a JSON object describing a directed graph of nodes. The engine recognizes the following top-level keys and ignores any others.
The engine validates the document on submission. The
nodes array must be non-empty, and there must be exactly one node of type lyzr-nodes-base.trigger; zero trigger nodes or more than one is an error. Connections whose source name does not match a real node are ignored, as are connection targets that are empty or reference a non-existent node.
Node object
Each entry innodes is an object with the following keys.
Connections and error edges
connections is keyed by the source node’s name, not its id. Each value is an object with two optional keys, main for the success path and error for the failure path. Both keys have the same shape: an array of output slots, where slot i (for example main[i]) is itself an array of target objects. The slot index is the source node’s output index.
Each target object has three keys.
When a node has
handleErrors: true in its parameters and fails permanently, downstream execution follows its error edges instead of aborting the run. Without handleErrors, a permanent failure aborts the run. error slots use the identical nested shape as main.
Loopback edges
Any edge that connects a node back to one of its upstream nodes is a loop. The target of that back-edge is the loop’s head, and the region between the head and the node that feeds the back-edge runs again each time items arrive on the back-edge. In the defaultwhile mode, the loop ends when the node that feeds the back-edge stops sending items to it. You configure each loop in settings.loopConfig, keyed by the name of its head node.
A loop whose region overlaps another loop’s region, or that sits inside a Loop node’s body, is reported as a cycle validation error. For a loop that stops on a condition or a budget, use the Loop Until node, whose Continue output is its back-edge.
Workflow settings
Thesettings object holds workflow-level settings. The following keys control loopback edges.
Each
loopConfig entry takes the following keys.
Expression syntax
Before a node runs, itsparameters are resolved recursively. For every string value at any depth, including strings nested in objects and arrays, the engine first strips a single leading = (the expression-mode prefix, as in "={{ $json.x }}"), then resolves every {{ ... }} block.
If the whole string after the = prefix is exactly one {{ ... }} block, the resolved value keeps its native type (number, boolean, object, or array). Otherwise each block is interpolated into the surrounding string: primitives become text, and objects and arrays are JSON-encoded. The practical consequence is that every string parameter on every node supports expressions.
The following forms are supported inside {{ }}.
Dot-paths traverse object keys and array indices, for example
results.0.name. A missing key or an out-of-range index yields null.
A bare reference such as $json.x, $('N').json.x, or $node[...] is resolved directly with its native type preserved. An expression containing operators, arithmetic, or concatenation, such as {{ $json.score % 2 }}, is evaluated as JavaScript after each reference is substituted with its JSON value; a runtime error yields null.
Loop variables
Inside a loop region, expressions can read$loop. Outside a loop, $loop is null.
$items is not available in workflow expressions. Only $json, $input, $node[...], and $(...) are supported. $items exists only inside the Code node’s JavaScript sandbox.Complete minimal example
The following document defines a trigger feeding a Set node, with onemain connection.
connections is keyed by node name (“Trigger”), not by id. main[0] is output slot 0, and its inner array holds the targets fed from that slot. The = prefix marks the Set value as an expression, so {{ $json.name }} resolves against the trigger’s output item.
Validation
Every workflow is checked against the published contract for each node type, as returned by the node catalog: which parameters a node accepts, what it emits, and how many inputs and outputs it has. The check runs in three places:- Validate Workflow runs it on demand without saving.
- Create Workflow and Update Workflow run it on save and return the result as
issues. The save still succeeds, so fix any errors before you run the workflow. - Execute Workflow refuses a run before it starts, with
400and{"error": "invalid workflow: ..."}, when a node reachable from the trigger has an unknown type or a connection from an output it does not have. Nodes that are not reachable from the trigger are ignored, so an orphaned node never blocks a run.
severity. An error means the workflow cannot run or a node is certain to fail, and a warning means it runs but probably not as intended. Errors are listed first.
The issue codes are invalid_json, no_nodes, trigger_count, unknown_node_type, missing_parameter, invalid_parameter, unknown_parameter, conditional_requirement, empty_configuration, duplicate_node_name, cycle, output_index_out_of_range, input_index_out_of_range, edge_from_terminal_node, edge_into_trigger, loop_without_body, unknown_connection_endpoint, unreachable_node, unwired_output, expression_unknown_node, expression_not_ancestor, and expression_unknown_field.
A few codes need more context:
unknown_parametercomes with ahintthat suggests the closest known parameter name.cycleflags a loopback edge whose region overlaps another loop’s region or sits inside a Loop node’s body.unreachable_nodeflags a node that is not connected to the Trigger, so it never runs.expression_unknown_node,expression_not_ancestor, andexpression_unknown_fieldcheck$('Node').json.fieldand$json.fieldreferences against what the upstream node actually emits.