data.nodes, parsedData, trigger, and every edge source / target.
Canvas data (React Flow toObject())
Root nodes:
data.nodes, not nested. They carry parentId. Positions are relative to the parent, not canvas coordinates. They are not draggable — the parent moves them.
Layout via API (autoLayout)
Send autoLayout: true on graph create and PUT. The server runs the same two board operations and does not store the flag:
- Sub-nodes —
draggable: false,extent: "parent",position.x = 138,position.yfrom the GUI stack (answers, then fallback/timeout). - Roots — dagre left-to-right (
ranksep~120,nodesep~60), same as the board Clean button. Children keep the relative positions from step 1.
parentId on buttons. You do not need to compute canvas x/y for roots or buttons. autoClean: true is accepted as an alias.
out edge from that parent. Execution continues from the sub-node connections.out.
Execution parsedData + trigger
transformReactFlowOutput builds this from the canvas:
- The trigger is removed from
parsedDataand stored asworkflow.trigger. triggerTypeistrigger.data.eventType.parsedDatais parent nodes only. Sub-nodes hang offparent.subNodes[].- Each node has
connections: { out: string[] }— target ids only. No handle ids. Branching is “which sub-node’sconnections.out”. - Runtime starts at
trigger.connections.out[0].
Parent
title is usually the NodeType string. Sub-node title is the canvas data.label. If/Else matches title.toLowerCase() === "true" / "false".Mapping checklist
How to wire a graph
Build the canvas first, then copy each edge into executionconnections.out. IDs must be identical in data.nodes, data.edges[].source / target, parsedData, trigger, and every connections.out entry.
1. Give every node a stable id
Use short unique strings (trig_1, wa_menu, ans_tours). Reuse the same string everywhere. Sub-nodes live in data.nodes as siblings with parentId set to the parent’s id.
2. Draw canvas edges (data.edges)
Every edge:
"out" edge from a parent that has sub-nodes. The engine ignores parent connections.out once subNodes exist and continues from the chosen child’s connections.out.
Fallback must not point at its parent. Never add a canvas edge fallback → parent and never put the parent id in fallback connections.out. Two modes (mutually exclusive — if shouldSendReplyMessage is true, runtime ignores connections.out and re-queues the same message node after sending the text):
3. Copy each edge into execution
For every canvas edgesource → target, find that source in trigger, parsedData[], or parent.subNodes[] and append target to connections.out (array of target ids only — no handles).
Runtime starts at
trigger.connections.out[0]. If that array is missing, the run never starts.
4. Sanity check
- Every
source/target/connections.outid exists. - Trigger is not in
parsedData. - Sub-nodes are not extra
parsedDataitems; they hang offparent.subNodes[]. - Parent with answers / true-false / cases has no root
outedge.