Workflows
A workflow is a visual map of what your agent should do step by step — who speaks first, what to ask, when to branch, when to transfer the call, and when to hand the call to a different agent. You build it on a canvas by dropping nodes and connecting them.
Use a workflow when a single prompt isn't enough — when the call has clear stages ("first qualify, then collect details, then either book or transfer"), when you need deterministic routing on the caller's answers (not "the AI usually does the right thing"), or when one agent should hand a slice of the work to a specialist.
A prompt is one long instruction. The agent reads it once and uses it for the whole call. A workflow is many small instructions, one per node, with explicit transitions between them. The graph drives the call — the agent on each node only owns its slice.
If your agent already works well with a single prompt, you don't need a workflow. Reach for workflows when you keep adding "if X then Y" rules to the prompt and they stop working reliably.
The Editor
The workflow editor lives in its own tab on the agent's page. You see a canvas with a Start node and an End node by default. You add nodes from the toolbar, drag them onto the canvas, and wire them with edges.
The right-hand panel switches as you select things:
- Click a node — its settings panel opens (prompt, fields, retrieval variables, etc.).
- Click an edge — its trigger settings open (AI decides / Condition / Always).
- The Variables panel (toolbar button) lists every variable the workflow declares or uses, with one-click jump to the node that sets or reads each one.
Run an in-browser Test Call straight from the editor — a glowing firefly tracks the live position so you can see exactly which node is active at each moment.
Workflows have three statuses:
- Draft — editable, not yet live. Use Test Call to try it.
- Active — live for real calls. The agent uses this workflow from the next call onwards.
- Template — a reusable blueprint, never directly linked to an agent. You can instantiate it onto multiple agents.
Nodes
Each node is a step in the conversation. Different node types do different things — some speak, some listen, some route, some run tools.
Agent
The agent node is the heart of a workflow stage. When the graph reaches an agent node, that agent takes the floor and drives the conversation until something triggers a transition.
You configure the agent node's persona and task right on the canvas (the Prompt tab opens when you click the node) — this is the prompt for that slice of the conversation, not the whole call. You can also attach knowledge bases and tools per-node.
Greeting — the opening line. If this is the entry node (the first stop after Start), this is what the caller hears first.
When a graph governs a call, the agent's main prompt (the one in the Model tab) is replaced by the active node's prompt. This is deliberate — the graph owns the personality and task per stage, not a single fixed prompt that competes with it.
Subagent
A subagent is a helper that runs inline — it takes over the conversation for a focused task, then hands the result back to whoever invoked it.
Subagents are great when one piece of work is reusable or feels distinct: "verify the caller's identity", "collect a shipping address", "qualify a lead with the SPIN method". The parent agent calls the subagent, the subagent does its thing and reports a result, the parent picks up where it left off.
Subagent nodes have the same tabs as an agent node (Prompt, Knowledge, Tools, Actions) — the editor creates a hidden backing agent for you automatically. You don't see this hidden agent in your main agent list, and it doesn't count against your plan's agent cap.
Return variable — the slot to store the subagent's result in (e.g. identity_verified, lead_score).
Message (Say)
The Say node speaks a fixed line, no AI turn, no improvisation. Use it for transitions ("One moment, transferring you now…"), legally required disclaimers, or canned confirmations where the wording matters.
Text — what to speak. Supports {{variable}} tokens to inject collected values, e.g. "Thanks {{first_name}}, I've booked you for {{appointment_time}}."
A Say node consumes the turn — the agent won't add anything else on top.
Gather Input
A Gather Input node asks one question and captures the caller's answer into a variable.
Prompt — the question to ask. Supports {{tokens}}.
Variable — the name of the slot to store the answer in.
Use it whenever you need a specific value before continuing — email, account number, party size, problem category. The caller's next turn becomes the value; you can branch on it with a Condition edge right after.
Update State
An Update State node writes one or more values into the workflow's variables without speaking or asking. Use it to pre-set defaults, mark progress flags, or stage values computed from prior steps.
Variable / Value — one key-value pair. More variables (optional) — additional key-value pairs.
Both keys and values support {{tokens}}, so you can compose new values from existing ones.
Condition
The Condition node is a routing-only node — it doesn't speak, doesn't ask, just sends the call down whichever outgoing branch matches.
The condition itself lives on each outgoing edge, not on the node — see Condition edges below.
Use a Condition node when you want a clear "fan-out" point in your graph: many branches that depend on one variable's value. Compared to putting conditions on agent-node edges, a dedicated Condition node makes the routing logic visually obvious.
Tool
The Tool node runs one of the agent's connected tools as a graph step — without giving the AI any say in whether to call it. Use it when you want a fact, not a conversation: "look up this customer", "check stock", "GET the current weather".
You can either:
- Pick one of the agent's existing tools (calendar lookup, knowledge-base search, an integration's specific action), or
- Configure an inline HTTP call right on the node (URL, method, headers, params, body).
The tool's response becomes available to all downstream nodes. If the response is a JSON object, each top-level field becomes its own variable — so a response like {"is_known": true, "tier": "gold"} writes both is_known and tier for you to branch on.
Every text field on the Tool node — URL, header values, body — supports {{tokens}}. Pull a variable into a URL path, a header, or a request body without writing any code.
If a tool call fails (HTTP error, timeout, network), the node logs the failure and the graph keeps going — without the data. Plan your downstream conditions to handle the "no value" case.
Integration
The Integration node is a focused variant of Tool, for connected external services — Google Calendar, Outlook, HubSpot. You pick the provider and the specific action (e.g. check_availability, create_event), set the parameters, and the node calls the integration deterministically.
If the integration isn't connected on this agent, the node passes through without doing anything — your graph continues. Check downstream conditions for the "didn't run" case.
Phone Transfer
The Phone Transfer node warm-transfers the call to an external phone number. After a successful transfer, the call has left your workflow — your agent is no longer on the line.
Number — destination in international format with a leading + (e.g. +15551234567). Supports {{tokens}} so the destination can come from an earlier step (e.g. the answer to "which office should we connect you to?").
If the transfer doesn't go through (invalid number, no answer, phone connection not set up), the graph rolls back and the current agent stays on the line. The conversation continues normally — the agent can apologise and try a different path.
End Call
The End Call node wraps up — the agent speaks the configured farewell, then disconnects. Use it as a clean terminus for "everything done" branches or for the "we can't help, sorry" branch from a hard-to-handle Condition.
No configuration needed beyond wiring an edge into it.
Reusing a Workflow (Sub-workflow)
A Sub-workflow node is reserved for embedding another workflow as a sub-graph. This node is in the editor but isn't fully wired yet — the graph passes through it without doing anything. Don't rely on it for production calls. We'll switch it on once we ship reusable sub-graphs.
Edges (Connections)
You wire one node to the next with an edge. The edge's trigger decides when the transition fires.
There are three triggers:
| Trigger | Who decides | When it fires |
|---|---|---|
| AI decides | The AI | When the conversation makes it the natural next step. The AI gets a "transition tool" it can call. |
| Condition | The runtime | When a variable matches a rule you write. Evaluated before the AI replies. |
| Always | The runtime | Unconditionally — the moment the source node finishes. No caller input, no AI involved. |
AI decides (intent-driven)
Use AI decides when only the AI can tell when to switch — "if the caller asks about pricing, transfer to the sales agent", "if it sounds like a complaint, run the apology subagent".
You write a short intent description on the edge (e.g. "Caller asks about pricing or wants to upgrade"). The AI sees it as a tool labeled with that intent and decides whether to call it based on the conversation. If multiple AI-decides edges exist, the AI picks at most one per turn.
Condition (deterministic branching)
Use Condition when you want the graph — not the AI — to decide based on a known fact: "if lead_score > 7, send to closer; otherwise, end call politely".
Conditions evaluate before the AI replies, so they always win over the AI on that turn. The AI doesn't get to override a matching condition.
You can build a condition out of these operators:
| Operator | Meaning | Example |
|---|---|---|
| equals | exact match (case-insensitive for text) | intent equals "billing" |
| not equals | doesn't match | status not equals "active" |
| contains | substring match (case-insensitive) | feedback contains "broken" |
| greater than | numeric > | lead_score greater than 7 |
| less than | numeric < | wait_minutes less than 5 |
You can also combine clauses with All (every clause must match — AND) or Any (at least one must match — OR), and nest them. So a rule like "the caller is known and their tier is gold or premium" becomes:
All:
- is_known equals true
- Any:
- tier equals gold
- tier equals premium
If you reference a variable that hasn't been set yet (e.g. the AI never collected it), conditions on it always fail — they don't error, they just don't match. So loyalty_tier equals "gold" returns false if loyalty_tier was never set. Design your branches to handle this — usually with an else branch (see below).
Else branch — when no condition matches
If you have several Condition edges leaving a node and none of them match, the call falls through to the lowest-priority non-Condition edge (Always, or an AI-decides edge). If there's no fallback at all, the AI regains control and can continue freely.
A common pattern: several Conditions for the well-known paths, plus an Always edge to a catch-all (Say something, then End Call, or transfer).
Always (auto-advance)
Use Always when the next step is unconditional — no decision, no caller input. Two natural uses:
- Chain steps: SAY → UPDATE_STATE → SAY → TOOL → Condition. Each step finishes and the Always edge moves on right away.
- Auto-advance after the agent finishes speaking: an agent node with an Always outgoing edge means "as soon as the AI stops talking, move on" — no need for the caller to respond. Useful for monologue-style flows like a presenter agent that delivers and then transitions to the next section.
Condition and Always (the deterministic ones) always win over AI decides. If a Condition matches, the AI's transition tools are ignored for that turn. This is what lets you write rules the AI can't bypass.
Variables
Variables are the workflow's memory. Most things in a workflow are about either setting a variable or reading one.
Declaring variables
The Variables panel (toolbar button) shows every variable in the workflow with the steps that set or read each one. Click a variable to jump to a setter; this is the fastest way to find where something is going wrong.
You can declare a variable with a default value in the panel — useful for flags like escalated=false that need to exist from the start so your conditions never silently fail on "missing".
How variables get set
| Source | Example |
|---|---|
| Default values (declared in the Variables panel) | escalated = false set before the call begins |
| Gather Input | The caller's spoken answer is captured into the node's variable |
| Update State | You write a value (literal or composed from {{tokens}}) directly |
| Tool / Integration result | The whole response is stored under the node's name; if the response is a JSON object, each top-level field also becomes its own variable |
| Subagent return | Whatever the subagent finishes with is stored in the subagent node's return variable |
Reading variables — {{tokens}}
Anywhere you can type text on a node — agent prompt, Say text, Gather prompt, Tool URL / body / headers, Phone Transfer number, Update State value, condition values — you can drop in {{variable}} and it'll be substituted at runtime.
Dotted paths also work for nested data — e.g. if a Tool response was {"customer": {"name": "Anna", "tier": "gold"}}, you can read {{customer.name}} or {{customer.tier}}.
If a token references a variable that doesn't exist, the literal {{name}} text is left in place (it isn't blanked out). This is intentional — it makes broken templates easy to spot in a test call, instead of silently swallowing them.
Common Patterns
Qualify → Branch → Route
Start
└─ Agent (greeting + qualify)
└─ Gather Input (intent)
└─ Condition: intent == "sales" → Agent (sales)
└─ Condition: intent == "support" → Subagent (triage)
└─ Always (catch-all) → Say "Let me connect you" → Phone Transfer
Lookup → Personalise
Start
└─ Tool (CRM lookup, writes is_known, name, tier)
└─ Condition: is_known == true → Agent (warm greeting with {{name}})
└─ Always (else) → Agent (cold greeting)
Reusable Helper (Subagent side-trip)
Agent (main conversation)
└─ AI decides: "Caller mentions an address" → Subagent (address collector)
└─ on finish: result = full_address
└─ Agent continues with {{full_address}}
What's Locked During a Workflow Call
A few defaults change when a graph governs the call:
- Language is locked to whatever the workflow's first agent uses. The AI cannot switch languages mid-call. This prevents the model from drifting into the wrong language on a noisy turn.
- The agent's main prompt is replaced by the active node's prompt. This is the "graph owns the persona" rule — see the warning under Agent.
- The first message comes from the entry node's greeting, not from the agent's main "Begin Message" field.
Loop Safety — Step Budget
Every workflow has a maximum number of transitions per call (default 25). Each time the graph moves to a new node, the counter goes down by one. When it reaches zero, the workflow stops jumping between nodes to prevent runaway loops.
You can pick a fallback node for this case (typically a Phone Transfer or End Call) — the call diverts there once the budget is gone. Otherwise the AI regains control and the graph effectively freezes for the rest of the call.
You very rarely need to think about this — it's a safety net for graphs that accidentally loop. If you hit it in normal use, you probably have a cycle somewhere.
Testing
The Test Call button in the editor opens an in-browser call with the current workflow draft. While the call runs, a glowing firefly moves between nodes on the canvas so you can see exactly where the call is. The editor is locked during a test call to prevent edits from desyncing with the live state.
Use Test Call to verify:
- Your conditions match the values you expect (watch the firefly skip past a branch you thought would fire).
- Your tokens substitute correctly (listen for stray
{{name}}if a value wasn't set yet). - Tool failures degrade gracefully (your fallback path actually fires).
Failure Modes Worth Knowing
| Situation | What happens |
|---|---|
| Tool / Integration call fails | The graph continues without the data. Your downstream conditions should handle the missing variable. |
| Phone Transfer fails (invalid number, no answer, phone connection missing) | The graph rolls back, the current agent stays on the line, and the conversation continues normally. |
| Subagent fails to start | The side-trip is cancelled, the parent agent stays on, and a short failure line is spoken. |
{{missing_variable}} in text | Left literal in the output — easy to spot during test calls. |
| Sub-workflow node | Currently passes through without doing anything. Don't rely on it yet. |
| Step budget exhausted | Diverts to the fallback node if one is set, otherwise the AI regains control. |
When NOT to use a Workflow
Workflows are powerful but they're not the right answer for every agent. Stick with a single prompt when:
- The conversation is open-ended ("answer the caller's questions about our products").
- You don't have clear-cut branching logic — most decisions are judgement calls the AI handles well.
- You'd be writing one agent node and one End Call — that's just a prompt with extra steps.
Reach for a workflow when:
- You can sketch the call as a flowchart with three or more distinct stages.
- You need at least one deterministic branch on a variable (a Condition).
- You want to reuse a slice of conversation across multiple agents (a subagent).
- You're already piling "if the caller says X then do Y" rules into a prompt and they're not reliable.