Edge structure
The JSON format for workflow edges — connections between nodes, handle IDs, and edge categories.
Every item in a workflow's edges array is a WorkflowEdge. It represents a connection between an output port on one node and an input port on another.
Schema
interface WorkflowEdge {
id: string;
source: string;
target: string;
sourceHandle?: string;
targetHandle?: string;
type?: ConnectionLineType;
selectable?: boolean;
deletable?: boolean;
data?: {
label?: string;
condition?: string;
metadata?: {
edgeType?: EdgeCategory;
sourcePortDataType?: string;
};
};
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique edge identifier (e.g., "e-loader-analyzer"). |
source | string | Yes | ID of the source node. |
target | string | Yes | ID of the target node. |
sourceHandle | string | No | ID of the specific output port. See Handle IDs. |
targetHandle | string | No | ID of the specific input port. See Handle IDs. |
selectable | boolean | No | Whether the edge can be selected in the editor. |
deletable | boolean | No | Whether the edge can be deleted by the user. |
data.label | string | No | Display label on the edge. |
data.condition | string | No | Condition expression (used with gateway branches). |
data.metadata.edgeType | EdgeCategory | No | Visual styling category. |
data.metadata.sourcePortDataType | string | No | Data type of the source output port. |
Handle IDs
Port handles follow a deterministic naming pattern:
{nodeId}-{direction}-{portId}For example:
content_loader.1-output-items— the "items" output port on nodecontent_loader.1analyzer.1-input-content— the "content" input port on nodeanalyzer.1router.1-output-high— the "high" branch output on a gateway node
This format lets FlowDrop map edges back to specific ports during rendering and execution.
Edge Categories
The edgeType field controls the visual style of the edge on the canvas:
| Category | Visual Style | When Used |
|---|---|---|
data | Solid gray line | Default — general data flow between nodes. |
trigger | Solid line with trigger color | Control flow connections (port dataType: "trigger"). |
tool | Dashed amber line | Tool interface connections (port dataType: "tool"). |
loopback | Dashed gray line | Loop iteration connections (targets a loop_back port). |
The editor sets edgeType automatically based on the source port's data type. You generally don't need to set it manually in JSON.
Unknown edge types are refused at save
This is a different field from the styling category above. The editor writes
its visual category at data.metadata.edgeType; a server reads the edge's
semantic type from data.edgeType, one level up, and falls back to the
target handle when it is absent. The styling category is never validated.
A server implementation may refuse a semantic data.edgeType it does not
recognise. The Drupal server does: a non-empty string there that is not
one of the values it knows fails validation with the code
R14_EDGE_TYPE_UNKNOWN at the locator edge.{index}, where {index} is the
edge's position in the edges array.
The values the Drupal server knows are data, trigger, tool_availability,
loopback and agent_result.
Absent, empty and non-string declarations are deliberately untouched: they are
not typos, and the handle-based classification they fall back to is the intended
default. The rule exists for the value that looks deliberate and is wrong — a
loopbak silently becomes an ordering edge in the compiled graph, and if it was
meant as a loopback it hard-errors much later as a forward cycle, naming the
cycle rather than the typo.
The check is save-only. The read path is unchanged, so a workflow already stored with an unrecognised value still loads, compiles and runs. Launching one is explicitly not blocked: the launcher re-validates every stored definition before running it — a definition can reach storage without passing the save path — and this rule is excluded from that gate. Bundle import and workflow migration do enforce it; both are writes.
To find stored workflows carrying a bad value, run the Workflow Doctor against them (Administration › FlowDrop › Workflows, then Doctor on the workflow). It reports the condition on an already-stored workflow, where a save would refuse it.
Examples
Data Edge
A simple data connection between two ports:
{
"id": "e-loader-analyzer",
"source": "content_loader.1",
"target": "analyzer.1",
"sourceHandle": "content_loader.1-output-items",
"targetHandle": "analyzer.1-input-content"
}Trigger Edge
A control-flow connection that triggers execution:
{
"id": "e-start-loader",
"source": "start.1",
"target": "content_loader.1",
"sourceHandle": "start.1-output-trigger",
"targetHandle": "content_loader.1-input-trigger",
"data": {
"metadata": {
"edgeType": "trigger",
"sourcePortDataType": "trigger"
}
}
}Tool Edge
A dashed connection linking an agent to a tool:
{
"id": "e-agent-tool",
"source": "search_tool.1",
"target": "agent.1",
"sourceHandle": "search_tool.1-output-tool",
"targetHandle": "agent.1-input-tool",
"data": {
"metadata": {
"edgeType": "tool",
"sourcePortDataType": "tool"
}
}
}Gateway Branch Edge
A conditional edge from a gateway branch:
{
"id": "e-router-high",
"source": "router.1",
"target": "urgent_handler.1",
"sourceHandle": "router.1-output-high",
"targetHandle": "urgent_handler.1-input-input",
"data": {
"condition": "priority > 8",
"metadata": {
"edgeType": "data"
}
}
}Connection Validation
The editor validates connections automatically before creating edges:
- Type compatibility — only compatible port data types can connect
- Cycle detection — prevents circular dependencies (O(V+E) algorithm)
- Loopback prevention — nodes cannot connect to themselves
For more on data type compatibility rules, see Port System & Data Types.
Next Steps
- Node Structure — the nodes that edges connect
- Port System & Data Types — port definitions and compatibility rules
- Workflow Structure — the top-level document containing edges