Router nodes are configured through the agent JSON / API today. Visual editor support is coming.
How a router node picks an edge
On entry, a router evaluates its edges in three phases and takes the first that resolves:| Order | Edge type | Cost |
|---|---|---|
| 1 | Expression edges, in priority order | Instant, no LLM call |
| 2 | Intent edges, via one routing-LLM call | One routing call (same as normal intent routing) |
| 3 | The unconditional catch-all | Instant fallback |
- The catch-all is always a fallback. Unlike a normal node, an unconditional edge on a router never pre-empts a matching expression edge, regardless of its
priority. It is taken only when nothing else matches (including when the intent call finds no match). - At most one routing-LLM call per turn. Every hop still checks its expression edges first; only the intent (LLM) call is capped. So if a router chains into another router that would also need an intent call, the second one takes its catch-all instead. A purely deterministic chain (expression + unconditional only) resolves in ~0ms with no LLM call at all.
priority orders edges within a phase (expression edges among themselves, intent edges among themselves). It cannot make an intent edge outrank an expression edge — expression edges are always checked first. If you want an intent decision to win a case, don’t also give the router an expression edge that matches that case.Configuring a router node
A router node is one entry in your agent’snodes array — the same array that holds every other node, under llm_agent.llm_config. See Full example for a complete agent payload, and Edges & routing for the expression syntax, operators, and built-in variables (like _total_turns) used below.
Set node_type to "router", leave the prompt empty, and give it edges plus an unconditional catch-all:
Field reference
| Field | Type | Required | What it does |
|---|---|---|---|
node_type | "router" | Yes | Marks the node as a silent dispatcher. |
edges | array | Yes | Expression, intent, and unconditional edges. Must include an unconditional catch-all. Event edges are not allowed. |
description | string | No | The classification objective shown to the routing LLM when the router has intent edges (a router has no prompt). |
prompt or static_message — it never speaks. On expression and unconditional edges, the condition string is just a human-readable label; only expression (for expression edges) and condition text on intent edges affect routing.
Intent routing on a router
An intent edge is an edge with acondition and no condition_type (the default edge type — see Edges & routing). A router can use intent edges to classify what the caller wants and dispatch silently. Because a router has no prompt, the routing LLM is guided by three things:
- Each intent edge’s
condition(and optionalfunction_description) — the primary signal. - The router’s
description— its overall objective. - The agent-level
routing_instructions, if set.
Example: silent NLU dispatcher
Classify the caller’s request and route to the right department. The escalation guard runs first (instant, no LLM), then the intent call, then the catch-all when the request is unclear.- If
_total_turns >= 12, the expression matches first and routes tohuman_handoffwith no LLM call. - Otherwise one routing call classifies the reply into
billing,tech_support, orsales. Intent edges can still extractparametersinto context, exactly like normal intent routing. - If the request is unclear, the catch-all routes to
clarify, which asks the caller to say what they need.
Example: pre-conversation dispatch
A router can be the start node. On the first turn it resolves before any conversational node runs, so the caller begins on the right node. Pre-conversation routing is deterministic (there is no reply to classify yet), so use expression and unconditional edges here.A router keys on call-time data. Pass it as
user_data when you start the call — e.g. { "user_data": { "customer_tier": "vip", "language": "hi" } } — and reference it in expressions as recipient_data.customer_tier, recipient_data.language, and so on. A value set in the agent’s context_data acts only as a default; on a real call the call-time user_data is what’s used.Validation
A router node config is checked when the agent is saved. It is rejected if:- It sets a
promptorstatic_message(a router never speaks). - It has no unconditional catch-all edge (a router must always be able to advance).
- Any edge is an event edge (a call never rests on a router, so events would never fire).
- An edge points to a node ID that doesn’t exist.
- Routers form a cycle among themselves with no way out to a speaking node.
When to use a router node
- Silent NLU dispatcher — classify a request once and route to the right sub-flow, without a node that speaks.
- Pre-conversation dispatch — pick the starting node from caller data before the first turn.
- Nested deterministic branching — keep complex rule-based routing in one place instead of copying edges across conversational nodes.

