Skip to main content
Back to how-to guides Use this pattern when the same request must reach a different agent by channel, language, user role, or session state.

Concept

A HANDOFF route can test any value in the session data. Write the test as an expression in WHEN. On each customer message, the platform checks the routes in the order you write them. It does this before the model reasons. The first route with a true expression takes the conversation. The model does not run for that message. The check skips a WHEN: true route and a route with a plain-language WHEN. The model can still call them. When no expression route is true, the model runs. The platform evaluates the WHEN of any route the model calls, and it refuses the route when the expression is false. Context routing differs from intent routing in one way. The classifier does not run when the supervisor has no intent.category condition and no INTENTS: section. Two tools control which routes can fire.

Values you can test

The platform provides channel, language, locale, time zone, and identity. It provides no role. Your channel, SDK, or a lookup must supply the role. Treat caller data from a browser as unverified. Pair a role test with session.identityTier == 2. Use session.channel for the channel. The paths channel.name and system.channel belong to other features. They do not resolve in a HANDOFF WHEN, and channel.name compiles without a warning. The path session.interaction.current.channel does not exist. Voice has several channel types: voice, voice_twilio, voice_livekit, voice_realtime, voice_pipeline, voice_vxml, korevg, audiocodes, and genesys_audio_connector. A test for "voice" matches only the first one. Use IN [...] to match the family.

Order and ties

The position of an entry sets its priority. The first entry has the highest priority. The PRIORITY: field is read and then ignored. Several expression routes can be true at once, for example a Spanish speaker on a voice channel. The first true route in the list wins. The model does not choose between them. Put the most specific route first. See Combine conditions.

Minimal working example

This supervisor routes verified administrators, Spanish speakers, and voice callers. A WHEN: true route handles everyone else. Put the supervisor and all agents in the same project. The target agents are short stand-ins. agents/context-routing-supervisor.agent.abl
agents/admin-support-agent.agent.abl
agents/spanish-service-agent.agent.abl
agents/voice-support-agent.agent.abl
agents/standard-service-agent.agent.abl
Project-local assumptions:
  • session.user_role is caller data that your SDK or channel sends, for example the value admin.
  • customer_id and account_id follow the same rules as in How to route users by detected intent. The platform passes the supervisor’s value when it has one. Otherwise the model fills the field from the conversation.

How it works

  1. The supervisor keeps the conversation until a route fires.
  2. On each customer message, the platform checks the expression routes in the order you wrote them. It skips WHEN: true routes, plain-language routes, and routes that ENABLED_WHEN hides.
  3. The first true route takes the conversation. The model does not run.
  4. When no expression route is true, the model runs. The model can call any available route. The platform evaluates that route’s WHEN at the call and refuses a false one.
  5. The target receives the PASS values, the summary, and the history that history allows. The default is full.
  6. With EXPECT_RETURN: false, the target keeps the conversation. With EXPECT_RETURN: true, the supervisor resumes when the target returns.
  7. A missing value makes a test false. session.user_role == "admin" is false when the role is absent.
  8. The model chooses the WHEN: true route. With ROLE: router, the supervisor must call a route or ask one question. Without BEHAVIOR, the supervisor can answer in text instead.
A route without an intent.category test never reads the category.

Common variations

Combine conditions

Join tests with AND and OR. Put the combined route before the single-test routes. Here, Spanish voice callers reach a dedicated agent.
Do not join a test with a quoted phrase. In session.channel == "voice" AND "the user is a VIP", the compiler warns with CEL_CONDITION_UNRESOLVED_CALL. At runtime the phrase is a string that is always true, so the route fires for every voice caller. Test a field instead, for example session.customer_tier == "vip".

Route on locale

Use the locale when the dialect matters.

Route on detected language

Use the detected language when the customer’s words decide the language, not the channel or profile setting. This needs the project setting for language detection. With the setting off, the value is absent and every test on it is false.
The platform detects the language of each customer message. It stores the result before it checks the routes, so the route reads the current message. The project settings set the confidence threshold, the minimum text length, and the allowed languages. A message that is too short, a keypad entry, or secure input does not give a detected status. An agent hook can also set the language with SET_INTERACTION_LANGUAGE. That changes session.interaction.current.language only when the agent’s before_turn hook runs. The check on the same message still reads the earlier value. Route on session.languageDetection for a decision on the current message.

Route on state that a child returns

Use EXPECT_RETURN: true when another agent collects the value first. This route asks for a language preference once. It then routes on the answer.
map copies the child’s chosen_language into the supervisor’s preferred_language. Write the child field first. The child must gather or produce chosen_language. Without a value, the first route fires again on the next message. After the child returns, resume_intent replays the customer’s request. The replay skips the check, so the model picks the route, and the route’s WHEN still applies. The next customer message goes through the check again. Use IS NOT SET on a plain variable such as preferred_language. Do not use it on a dotted path such as session.interaction.current.language. The path holds a null value when the language is unknown, and IS NOT SET is false for null.

Hide a route on other channels

Add ENABLED_WHEN when a channel must never see a route. The route is out of the check and out of the model’s choices while the condition is false.
Write ENABLED_WHEN as an expression over data that exists before the turn. A condition that reads intent or plain language fails to compile with ENABLED_WHEN_SEMANTIC_REFERENCE. A condition that reads a missing value counts as false.

Add ON_FAILURE

ON_FAILURE runs when the handoff fails before the target accepts it.
The values are CONTINUE, ESCALATE, and RESPOND "message". A bare quoted string fails with INVALID_HANDOFF_FAILURE_ACTION.

Verification

  1. Start a voice session and send a message. Open the trace. Find the deterministic_routing event. Confirm target is Voice_Support_Agent and condition shows the voice test.
  2. If the route does not fire, the model path runs. Find the handoff_condition_check event for a route the model called. Read evaluatedContext to see the value of session.channel that your channel reports.
  3. Send a Spanish message on a web channel. Confirm deterministic_routing names Spanish_Service_Agent. For the detected-language route, find the language_detection event. Confirm status is detected.
  4. Start a session with caller data user_role set to admin and an identity tier of 2. Confirm Admin_Support_Agent. Repeat with tier 0. Confirm the route does not fire.
  5. Send a message that matches no condition. Confirm the model calls Standard_Service_Agent. Find its handoff_condition_check event.
  6. For a route with EXPECT_RETURN: true, find the handoff_completed event. Read outcome and durationMs.
  7. Check that each target receives the pass values and the summary.

Production readiness checklist

  • Every value in a WHEN has a source: the platform, caller data, a lookup, or a child’s return.
  • Role tests also require a verified identity, for example session.identityTier == 2.
  • Channel tests list every channel type in the family, or you confirm the exact value in a trace.
  • Overlapping routes are ordered with the most specific route first.
  • A WHEN: true route covers every other message, and BEHAVIOR with ROLE: router stops the supervisor from answering in text.
  • Detected-language routes test status. A fallback route handles the case where detection is off.
  • Quoted phrases never appear inside an AND or OR test.
  • ON_FAILURE is set on routes where a missing target must produce a reply.
  • Tests cover each channel, each language, each role, and one message with no match.

Common mistakes

Troubleshooting