Define Control-Flow and Data-Flow Edges
A Flow uses two independent kinds of connections. Control-flow edges describe the nodes that can execute after a node completes. Data-flow edges transfer named values between declared component outputs and inputs.
Most Flows use both kinds of connections, but they serve different purposes:
| Connection | Describes |
|---|---|
ControlFlowEdge | A possible control continuation from one node to another. |
DataFlowEdge | The transfer of one named output value to one named input. |
These relationships are explicit. Matching node or input names do not create a connection, and a control-flow edge does not imply a corresponding data-flow edge.
ControlFlowEdge
A ControlFlowEdge identifies a source node, a destination node, and, where applicable, a branch selected by the source node. After the source node completes, the edge describes a destination that can execute next.
ControlFlowEdge Fields
| Field | Requirement | Description |
|---|---|---|
component_type | Required | Identifies the component as a ControlFlowEdge. |
id | Required | Uniquely identifies the edge within its component scope. |
name | Required | Provides a human-readable component name. |
from_node | Required | References the node from which control can continue. |
to_node | Required | References a node that can execute when this edge applies. |
from_branch | Optional | Identifies the source branch associated with this edge. |
The following edge describes process-request as a possible next node after start completes:
{
"component_type": "ControlFlowEdge",
"id": "start-to-process-request",
"name": "start_to_process_request",
"from_node": {
"$component_ref": "start"
},
"to_node": {
"$component_ref": "process-request"
}
}
from_node and to_node must resolve to nodes in the same Flow graph. The edge describes a control relationship only; it does not supply any input values required by process-request.
Branch-Specific Control Flow
A node with multiple control branches can have several outgoing ControlFlowEdge components. Each edge uses from_branch to associate one declared branch with a possible next node.
For example, the following edges connect the eligible and ineligible branches of check-return-eligibility to different nodes:
[
{
"component_type": "ControlFlowEdge",
"id": "eligibility-to-create-return",
"name": "eligibility_to_create_return",
"from_node": {
"$component_ref": "check-return-eligibility"
},
"from_branch": "eligible",
"to_node": {
"$component_ref": "create-return"
}
},
{
"component_type": "ControlFlowEdge",
"id": "eligibility-to-explain-denial",
"name": "eligibility_to_explain_denial",
"from_node": {
"$component_ref": "check-return-eligibility"
},
"from_branch": "ineligible",
"to_node": {
"$component_ref": "explain-denial"
}
}
]
Every declared branch that can be selected must have an outgoing ControlFlowEdge whose from_node references the branching node and whose from_branch has the exact same name. Control through each branch must eventually reach an EndNode.
Selecting a branch does not transfer values to its destination. Separate data-flow edges supply the inputs required by the node on that branch.
FlowNode Branches
A FlowNode can select a branch according to the branch_name of the EndNode reached in its subflow. An outgoing control-flow edge in the enclosing Flow uses that branch name in from_branch to describe the possible continuation.
The EndNode belongs to the subflow graph and is not referenced by control-flow edges in the enclosing Flow. Instead, an outgoing edge in the enclosing Flow references the FlowNode in from_node and uses the EndNode's branch_name in from_branch. For more information, see Subflow Completion and Branch Selection.
DataFlowEdge
A DataFlowEdge transfers one value from a declared source output to a declared destination input. It identifies both components and the named output and input involved in the transfer.
DataFlowEdge Fields
| Field | Requirement | Description |
|---|---|---|
component_type | Required | Identifies the component as a DataFlowEdge. |
id | Required | Uniquely identifies the edge within its component scope. |
name | Required | Provides a human-readable component name. |
source_node | Required | References the component that produces the value. |
source_output | Required | Names an output declared by the source component. |
destination_node | Required | References the component that receives the value. |
destination_input | Required | Names an input declared by the destination component. |
The following edge transfers order_id from start to lookup-order:
{
"component_type": "DataFlowEdge",
"id": "order-id-to-lookup",
"name": "order_id_to_lookup",
"source_node": {
"$component_ref": "start"
},
"source_output": "order_id",
"destination_node": {
"$component_ref": "lookup-order"
},
"destination_input": "order_id"
The endpoint names are case-sensitive and must exist in the applicable input and output contracts. The runtime validates that the transferred value conforms to the destination input declaration.
The edge makes order_id available to lookup-order, but it does not determine when lookup-order can execute.
Independent Control and Data Connections
Control-flow and data-flow edges do not require a one-to-one correspondence. A value can pass directly from its producer to a later consumer without being copied through every node on the control path.
Consider an order-support Flow that validates a request, looks up the order, formats a response, and then completes:

Description of the illustration Control-Flow and Data-Flow Edge Mapping
The corresponding control-flow connections are:
[
{
"component_type": "ControlFlowEdge",
"id": "start-to-validate",
"name": "start_to_validate",
"from_node": {
"$component_ref": "start"
},
"to_node": {
"$component_ref": "validate-request"
}
},
{
"component_type": "ControlFlowEdge",
"id": "validate-to-lookup",
"name": "validate_to_lookup",
"from_node": {
"$component_ref": "validate-request"
},
"to_node": {
"$component_ref": "lookup-order"
}
},
{
"component_type": "ControlFlowEdge",
"id": "lookup-to-format",
"name": "lookup_to_format",
"from_node": {
"$component_ref": "lookup-order"
},
"to_node": {
"$component_ref": "format-response"
}
},
{
"component_type": "ControlFlowEdge",
"id": "format-to-end",
"name": "format_to_end",
"from_node": {
"$component_ref": "format-response"
},
"to_node": {
"$component_ref": "end"
}
}
]
The data-flow connections are:
[
{
"component_type": "DataFlowEdge",
"id": "request-to-validation",
"name": "request_to_validation",
"source_node": {
"$component_ref": "start"
},
"source_output": "request",
"destination_node": {
"$component_ref": "validate-request"
},
"destination_input": "request"
},
{
"component_type": "DataFlowEdge",
"id": "order-id-to-lookup",
"name": "order_id_to_lookup",
"source_node": {
"$component_ref": "start"
},
"source_output": "order_id",
"destination_node": {
"$component_ref": "lookup-order"
},
"destination_input": "order_id"
},
{
"component_type": "DataFlowEdge",
"id": "request-to-formatter",
"name": "request_to_formatter",
"source_node": {
"$component_ref": "start"
},
"source_output": "request",
"destination_node": {
"$component_ref": "format-response"
},
"destination_input": "request"
},
{
"component_type": "DataFlowEdge",
"id": "status-to-formatter",
"name": "status_to_formatter",
"source_node": {
"$component_ref": "lookup-order"
},
"source_output": "order_status",
"destination_node": {
"$component_ref": "format-response"
},
"destination_input": "order_status"
},
{
"component_type": "DataFlowEdge",
"id": "response-to-end",
"name": "response_to_end",
"source_node": {
"$component_ref": "format-response"
},
"source_output": "response",
"destination_node": {
"$component_ref": "end"
},
"destination_input": "response"
}
]
During execution:
- The
start-to-validatecontrol-flow edge identifiesvalidate-requestas the first possible node afterstart. - The
request-to-validationdata-flow edge supplies the request required byvalidate-request. - After validation completes, the
validate-to-lookupcontrol-flow edge identifieslookup-orderas a possible next node. lookup-order.order_idcomes directly fromstart.order_id; the validation node does not need to accept and republish that value.format-responsereceives the original request directly fromstartand the order status fromlookup-order.- After control reaches
format-response, both values are available through its declared inputs. - The formatted response is transferred to
end.response, and control continues toendthrough the separateformat-to-endedge.
This separation avoids pass-through inputs and outputs on nodes that do not use or transform a value. Each node declares only the data it consumes or produces, while the control graph independently describes the possible execution sequence.