Overview
A flow consists of:- Begin — which node starts the conversation and who speaks first
- Nodes — individual steps (conversation, tool call, logic branch, transfer, etc.)
- Edges — connections between nodes that define transitions and conditions
Flow Definition Reference
Top-Level Structure
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion | 1 | Yes | Must be 1 |
begin | object | Yes | Flow entry point configuration |
nodes | array | Yes | List of flow nodes (at least one) |
edges | array | Yes | List of edges connecting nodes |
ui | object | No | Visual editor state (viewport position/zoom) |
Begin
| Field | Type | Required | Description |
|---|---|---|---|
startNodeId | string | Yes | ID of the first node to execute. Must match a node in nodes. |
whoSpeaksFirst | "agent" or "user" | Yes | Whether the agent speaks first or waits for the caller |
Node Types
Every node has the following base fields:| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for the node |
type | string | Yes | One of the node types below |
name | string | Yes | Human-readable label |
isGlobal | boolean | No | If true, this node can be triggered from any point in the flow via a global edge |
position | { x, y } | Yes | Visual position in the editor (use any values when generating programmatically) |
data | object | Yes | Type-specific configuration (see below) |
Conversation
The core node type. The agent speaks and/or listens based on an instruction. Type:"conversation"
| Field | Type | Required | Description |
|---|---|---|---|
instructionType | "prompt" or "static" | Yes | "prompt" = LLM interprets the instruction as context. "static" = agent speaks the text verbatim. |
instruction | string | Yes | The instruction text or static message. Supports {{variables}}. |
skipResponse | boolean | No | If true, the agent speaks the instruction and immediately moves to the next node without waiting for a user reply. Requires a skip edge. |
blockInterruptions | boolean | No | If true, the agent cannot be interrupted while speaking |
Function (Tool Call)
Invokes one of your agent’s custom tools during the flow. Type:"function"
| Field | Type | Required | Description |
|---|---|---|---|
toolName | string | Yes | Name of the custom tool to invoke (must be configured on the agent) |
speakDuringExecution | boolean | No | If true, agent speaks while the tool runs |
speakInstruction | string | No | What to say while the tool executes |
speakInstructionType | "prompt" or "static" | No | How to interpret the speak instruction |
blockInterruptions | boolean | No | Block interruptions during execution |
waitForResult | boolean | No | Default true. Set false for fire-and-forget calls. |
outputVariables | array | No | Map tool output keys to flow variables for use in downstream conditions |
outputVariables:
| Field | Type | Description |
|---|---|---|
outputKey | string | Key in the tool’s JSON response |
variableName | string | Flow variable name to store the value as |
Logic Split
A branching node with no data of its own — all logic is defined by its outgoing edges (conditions and an else fallback). Type:"logic_split"
| Field | Type | Required | Description |
|---|---|---|---|
| (none) | — | — | Data is an empty object {} |
- One or more
conditionedges (evaluated in order) - Exactly one
elseedge (fallback if no conditions match)
Call Transfer
Transfers the call to another phone number. Type:"call_transfer"
| Field | Type | Required | Description |
|---|---|---|---|
transferTo | string | Yes | Destination phone number in E.164 format (e.g. "+14155551234") or a {{variable}} |
transferMode | "cold" or "warm" | No | Cold = immediate transfer. Warm = agent briefs the recipient first. |
speakDuringExecution | boolean | No | Speak while the transfer is being set up |
speakInstruction | string | No | What to say during transfer |
holdMessage | string | No | Message spoken to the caller while on hold (warm transfer, max 500 chars) |
holdMusicEnabled | boolean | No | Play hold music (warm transfer) |
summaryPrompt | string | No | Prompt used to generate a brief for the recipient (warm transfer, max 2000 chars) |
introMessage | string | No | Message spoken to the recipient before connecting (warm transfer, max 500 chars) |
End Call
Terminates the call, optionally speaking a closing message. Type:"end"
| Field | Type | Required | Description |
|---|---|---|---|
message | string | No | Closing message to speak before hanging up |
Press Digit (DTMF)
Waits for the caller to press a phone keypad digit. Useful for IVR-style menus or entering account numbers. Type:"press_digit"
| Field | Type | Required | Description |
|---|---|---|---|
instruction | string | Yes | What to say before listening for a digit |
detectionDelaySeconds | number | No | How long to wait for input (0–10 seconds, default 1) |
Extract Variable
Uses the LLM to extract structured data from the conversation and store it in flow variables for downstream use. Type:"extract_variable"
| Field | Type | Required | Description |
|---|---|---|---|
variables | array | Yes | List of variables to extract (at least one) |
variables:
| Field | Type | Required | Description |
|---|---|---|---|
variableName | string | Yes | Name of the flow variable to store the extracted value |
description | string | Yes | Description of what to extract (used as context for the LLM) |
variableType | "text", "number", "enum", "boolean" | Yes | Data type of the variable |
enumOptions | string[] | No | Required when variableType is "enum" — the allowed values |
Edges
Edges connect nodes and define how the flow transitions between steps.Condition edges can be attached to any node type, not just logic split nodes. For example, you can put condition edges directly on a conversation node to branch based on the caller’s response — no logic split needed.
Edge Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for the edge |
source | string | Yes | ID of the source node, or "__global__" for global edges |
target | string | Yes | ID of the target node |
kind | string | Yes | One of: "default", "condition", "else", "skip" |
order | integer | Condition edges only | Evaluation order (0-based). Lower numbers are evaluated first. |
condition | object | Condition edges only | The condition to evaluate |
Edge Kinds
| Kind | Description |
|---|---|
default | Unconditional transition — used when a node completes normally |
condition | Evaluated in order; first matching condition wins |
else | Fallback when no condition edges match (required on logic_split nodes) |
skip | Used with skipResponse conversation nodes — immediate transition without waiting for user input |
Conditions on Any Node
You can attach condition edges to any node type. This is particularly useful on conversation nodes — branch directly based on the caller’s response without needing a separate logic split node. For example, a greeting node that routes callers to different paths:Conditions
Conditions determine whether acondition edge is followed. There are two types:
Prompt Conditions
The LLM evaluates a natural language question against the conversation context.| Field | Type | Description |
|---|---|---|
type | "prompt" | Prompt-based condition |
promptText | string | A yes/no question the LLM evaluates against the conversation |
Equation Conditions
Variable-based conditions that compare flow variables against values. No LLM call required.| Field | Type | Description |
|---|---|---|
type | "equation" | Equation-based condition |
match | "any" or "all" | Whether any or all equations must be true. Optional — defaults to "all". |
equations | array | List of equations to evaluate |
| Field | Type | Description |
|---|---|---|
variable | string | Flow variable name to compare |
operator | string | Comparison operator |
value | string | Value to compare against (not required for exists/not_exists) |
==, !=, contains, not_contains, contained_in, not_contained_in, >, <, >=, <=, exists, not_exists
Global Nodes and Edges
Global nodes can be triggered from any point in the conversation, not just from a specific predecessor node. This is useful for handling requests that can happen at any time, such as “transfer me to a human” or “I want to cancel.” To make a node global:- Set
isGlobal: trueon the node - Add one or more edges with
source: "__global__"targeting that node - Global edges must have a
condition— typically a prompt condition
Transition Behavior
After each user reply, the agent evaluates edges in this priority order:- Global edges — checked first, across all global nodes. If a global condition matches, the flow jumps to that global node regardless of where the conversation currently is.
- Condition edges on the current node — evaluated in
order, first match wins - Else edge — taken if no condition edges matched
- Default edge — unconditional, taken if present
- No match — the agent stays in the current node and continues the conversation
Validation Rules
When submitting a flow via the API, the following rules are enforced:schemaVersionmust be1- Every node must have a unique
id begin.startNodeIdmust reference an existing node- All edge
sourceandtargetvalues must reference existing nodes (except"__global__"as source) logic_splitnodes must have exactly oneelseedgeskipResponseconversation nodes must have exactly oneskipedge and no other outgoing edges- Global nodes (
isGlobal: true) must have at least one__global__edge targeting them __global__edges must target nodes withisGlobal: trueconditionedges must have anordervalue, unique per source node- Prompt conditions must have non-empty
promptText; equation conditions must have at least one equation - Total flow size must not exceed 48 KB
Complete Example
Here is a complete flow for an order status hotline:- Greets the caller and asks for their order number
- Extracts the order number from the conversation
- Calls a tool to look up the order status
- Branches based on whether the status is
"shipped"or anything else - Responds with the appropriate message
- Ends the call
- At any point, if the caller asks for a human, the call is transferred (global node)
Importing Flows via API
Agentmode is set at creation time and cannot be changed afterwards. To use conversation flows, create an agent with mode: "conversation_flow".
Creating a flow agent
Use the Create Agent endpoint withmode set to "conversation_flow" and the flowDefinition field set to your flow JSON:
Updating a flow
To update the flow on an existingconversation_flow agent, use the Update Agent endpoint with just the flowDefinition field:
mode is immutable after creation. You cannot convert a single_prompt agent to conversation_flow — create a new agent instead.Using Variables in Flows
Flow nodes support{{variable}} syntax in instruction text. Variables can come from:
- System variables (
{{current_time}},{{user_number}}, etc.) — see Prompting & Variables - Default variables configured on your agent
- Dynamic variables passed via the API or pre-call webhook
- Extracted variables from
extract_variablenodes - Tool output variables mapped via
outputVariablesin function nodes