Skip to navigation

Router Step

Router steps send each workflow run down the first branch whose condition matches. Use a Router when the workflow needs more than a pass/fail decision, such as routing invoices by value, document type, region, or exception category.

Use Router for new branching logic. Existing legacy Conditional steps continue to work, but Composer chat does not create or edit them.

How routing works

Each branch has a name and one predicate block. Like a Validation step, that block can use JavaScript code or an LLM-powered semantic check.

Branches run from top to bottom:

  1. The Router evaluates the first branch.
  2. If it matches, the run follows that branch and later branches are not evaluated.
  3. If it does not match, the Router evaluates the next branch.
  4. If no branch matches, the run follows the Else output. Leaving Else unconnected stops the workflow at the Router.

This makes branch order significant. Put the most specific or highest-priority conditions first.

Via the API

Add a step with type: "ROUTER" to the workflow’s steps array. Each entry in config.branches has a stable id and a code or semantic predicate in block. Connect a branch to its destination with next[].branchId.

Define ordered branches and the fallback

This step assumes an upstream extractor named extract_invoice produces a numeric total and a vendor_name. The first branch uses code to select high-value invoices; the second uses a semantic check to select suspicious vendor names.

{
"name": "route_invoice",
"type": "ROUTER",
"config": {
"branches": [
{
"id": "high_value",
"name": "High value",
"block": {
"name": "High value threshold",
"blockType": "code",
"code": {
"language": "javascript",
"code": "const total = context.outputs.extract_invoice.total.value; return [total != null && total >= 10000, 'Review invoices of at least 10000'];"
}
}
},
{
"id": "suspicious_vendor",
"name": "Suspicious vendor",
"block": {
"name": "Vendor check",
"blockType": "semantic",
"semantic": {
"promptText": "The vendor name {{ outputs.extract_invoice.vendor_name.value }} is suspicious or is a placeholder rather than a plausible company name."
}
}
}
]
},
"next": [
{ "step": "finance_review", "branchId": "high_value" },
{ "step": "fraud_review", "branchId": "suspicious_vendor" },
{ "step": "send_response", "branchId": "otherwise" }
]
}
FieldHow to use it
nameA unique step name, from 1 to 255 characters. Use this name in upstream next[].step entries.
config.branchesFrom 1 to 100 branches, in evaluation order. The first matching predicate selects its route.
branches[].idRequired, non-empty, and unique within this Router. Use the same value in next[].branchId. "otherwise" is reserved and cannot be a configured branch ID.
branches[].nameOptional display name, up to 255 characters. Omit on update to preserve the existing name.
branches[].blockA code or semantic block with name, blockType, and the matching code or semantic object. Block id is optional on input and generated when omitted.
next[].stepThe name of a downstream step in the same workflow.
next[].branchIdA configured branch ID, or "otherwise" for the Else route.

Use language: "javascript" for code predicates, and return true, false, or [boolean, "reason"]. A route name is not a valid return value. Semantic predicates use a non-empty semantic.promptText with {{ outputs.<step>.<field>.value }} templates. Both address upstream steps by their public names; see Code branches and Semantic branches for context and template details.

The fallback is implicit: do not add an otherwise entry to config.branches. Add a next entry with branchId: "otherwise" to connect it. If no branch matches and this route is unconnected, processing stops at the Router. A predicate execution error fails the step instead of selecting the fallback.

Save, deploy, and read results

  1. Include this definition in the complete steps array when calling Create Workflow (workflows.create) or Update Workflow (workflows.update). Include the trigger, parse, upstream extraction, and every downstream step, and connect the upstream step to route_invoice. Updating steps replaces the entire draft step list.
  2. To preserve an existing Router’s branches, keep its step name and omit config. Providing config.branches replaces the branch list and its order. Keep branch IDs stable when renaming or reordering branches. Omitting block preserves an existing branch’s predicate; a new branch with no block receives a default predicate that returns true, so supply a predicate for every new branch.
  3. Create a Workflow Version (workflowVersions.create) to deploy the draft. Passing steps deploys those definitions directly without modifying the draft.
  4. Run the workflow, then retrieve the run (workflowRuns.retrieve). Find the stepRuns entry with stepType: "ROUTER" and step.name: "route_invoice".

The step’s result.matchedBranch contains the selected branch’s id and name, or null when no branch matched. result.branches contains only evaluated branches, in order, each with id, name, matched, and optional reasoning or error. Later branches are not evaluated after a match. The entire result is nullable for a step run without a result.

See Router in the step reference for the configuration contract and deployment workflow.

Via Extend Studio

Build a Router with Composer chat

Open the workflow you want to edit in Extend Studio and open Composer in the workflow builder. The same chat panel can build Router branches and Validation blocks. You need workflow edit access for Composer to change the draft.

Workflow builder showing the Composer chat panel beside step configuration, with the prompt box, approval mode, and Review and Undo controls

  1. Start a new chat with +, or continue the current conversation. Composer uses the workflow displayed in the builder.
  2. Choose Ask for approval or Edit automatically beneath the message box. Ask for approval lets you inspect and accept a proposed update; Edit automatically applies changes to the draft while Composer works.
  3. Name the upstream step, list the branches in evaluation order, choose code or semantic logic for each branch, and specify every destination, including Else. Name the upstream fields when you know them; Composer can inspect the available variables.
After extract_invoice, add a Router named route_invoice with these branches in order:
1. High value: use code to match total values of at least 10,000 and route to finance_review.
2. Suspicious vendor: use a semantic check for suspicious or placeholder vendor_name values and route to fraud_review.
Route everything else through Else to send_response. Create any missing downstream review or response steps.
  1. Open Review on the proposed update. Confirm the predicates, branch order, branch connections, and Else destination. Accept the proposal if you chose Ask for approval; use Undo to revert an applied change.
  2. Refine individual branches in the same conversation without rebuilding the Router.
Change the High value branch threshold to 25,000, then move Suspicious vendor above it. Keep the branch destinations and Else route unchanged.
  1. Select the Router node and Configure to inspect the generated predicates. Test documents that match each branch and one that matches neither. Inspect the selected route before deploying the workflow.

The chat history menu lets you reopen a saved conversation to continue edits. See Composer chat for permissions, approval modes, and history.

Composer chat history menu showing saved workflow conversations and the search box for reopening a previous chat

Configure branches manually

  1. Add a Router node and connect an upstream step to it.
  2. Select Configure.
  3. Name the first branch, choose Code or Semantic, and define when it should match.
  4. Select Add branch for each additional route.
  5. Drag branches into evaluation order.
  6. On the workflow diagram, connect each named output and the Else output to downstream steps. Changes save automatically.

Code branches

Return true to select the branch or false to continue to the next branch. Return [boolean, "reason"] to include an explanation in the run result.

const total = context.outputs.extract_invoice.total.value;
if (total == null) return [false, "No invoice total"];
return [total >= 10000, `Invoice total is ${total}`];

Router code predicates accept boolean results only. Do not return a route name from the code—the matching branch already determines the route.

Router code uses the same context shape as Validation code blocks. In a multi-file workflow after a Collect step, use context.outputs.<step> for aggregate checks and context.files[i].outputs.<step> to associate an output with a specific file. The aggregate output arrays are not index-aligned with context.files, because routing may skip a step for some files.

Semantic branches

Write a yes-or-no criterion and optionally insert upstream values with {{ }} templates. Variables inside the templates are formatted and queried using JMESPath.

The applicant names in {{ outputs.extract_loan_application.applicant_names.value }} match the borrower names on the promissory notes in {{ outputs.extract_mortgage_package.promissory_note_borrower_names.value }}.

A yes result selects the branch. A no result continues to the next branch.

View run results

Open a completed workflow run and select the Router step. The result shows the selected route and each branch that was evaluated before the match. Code reasons and semantic reasoning appear with their branch results. If no branch matched, the result shows that the run followed Else.