Example 5: Conditional Branching
Building on lifecycle actions, this example adds smart routing—different follow-up paths based on user preferences.
Objective
In this example, you'll learn:
- How to use conditional
nextentries for branching logic - How to write JMESPath expressions for conditions
- How to create parallel workflow paths with different endings
- The importance of fallback routes
The Scenario
Your contact form collects the user's preferred contact time. Now you want to:
- Route morning/afternoon preferences → schedule a phone call
- Route evening/night preferences → send an email follow-up
This creates personalized experiences based on user input.
Implementation
Here's the complete tool definition:
{
"type": "context",
"context": {
"task": {
"type": "steps",
"version": "v1alpha",
"id": "contact-form-with-branching",
"tool": {
"name": "submit_contact_info",
"description": "Submit contact form information with conditional routing"
},
"steps": [
{
"id": "COLLECT_NAME",
"goal": "Collect the user's name",
"instructions": [
"Ask the user for their name.",
"Once you have their name, call the submit_contact_info tool with the user_name parameter."
],
"inputs": [
{
"name": "user_name",
"type": "string",
"description": "The user's name",
"required": true
}
],
"on": {
"enter": [
{"action": "say", "text": "Welcome! Let's collect your contact information. Step 1 of 3."},
{"action": "set", "name": "local.steps_completed", "value": 0}
],
"submit": [
{"action": "save"},
{"action": "inc", "name": "local.steps_completed"}
]
},
"next": [{"id": "COLLECT_EMAIL"}]
},
{
"id": "COLLECT_EMAIL",
"goal": "Collect the user's email address",
"instructions": [
"Ask the user for their email address.",
"Once you have their email, call the submit_contact_info tool with the user_email parameter."
],
"inputs": [
{
"name": "user_email",
"type": "string",
"description": "The user's email address",
"required": true
}
],
"on": {
"enter": [{"action": "say", "text": "Great! Step 2 of 3."}],
"submit": [
{"action": "save"},
{"action": "inc", "name": "local.steps_completed"}
]
},
"next": [{"id": "COLLECT_TIME"}]
},
{
"id": "COLLECT_TIME",
"goal": "Collect the user's preferred contact time",
"instructions": [
"Ask the user for their preferred contact time.",
"Once you have their preference, call the submit_contact_info tool with the contact_time parameter."
],
"inputs": [
{
"name": "contact_time",
"type": "string",
"description": "The user's preferred contact time",
"required": true,
"enum": ["morning", "afternoon", "evening", "night"]
}
],
"on": {
"enter": [{"action": "say", "text": "Almost done! Step 3 of 3."}],
"submit": [
{"action": "save"},
{"action": "inc", "name": "local.steps_completed"}
]
},
"next": [
{
"if": "contact_time == 'morning' || contact_time == 'afternoon'",
"id": "SCHEDULE_CALL"
},
{
"if": "contact_time == 'evening' || contact_time == 'night'",
"id": "SEND_EMAIL"
},
{
"id": "SEND_EMAIL"
}
]
},
{
"id": "SCHEDULE_CALL",
"goal": "Confirm phone follow-up for daytime contact preference",
"instructions": [
"Inform the user that we'll call them during business hours (morning or afternoon).",
"Thank them for providing their information.",
"Call the submit_contact_info tool to complete this step."
],
"inputs": [],
"next": []
},
{
"id": "SEND_EMAIL",
"goal": "Confirm email follow-up for evening/night contact preference",
"instructions": [
"Inform the user that we'll send them an email since they prefer evening or night contact.",
"Thank them for providing their information.",
"Call the submit_contact_info tool to complete this step."
],
"inputs": [],
"next": []
}
]
}
},
"tool": {
"type": "function",
"function": {
"name": "contact_form_branching_workflow",
"description": "Multi-step workflow with conditional branching",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
}
}
Key Concepts
Conditional next Entries
The next array can contain multiple entries with conditions:
"next": [
{
"if": "contact_time == 'morning' || contact_time == 'afternoon'",
"id": "SCHEDULE_CALL"
},
{
"if": "contact_time == 'evening' || contact_time == 'night'",
"id": "SEND_EMAIL"
},
{
"id": "SEND_EMAIL"
}
]
Evaluation rules:
- Entries are evaluated in order (top to bottom)
- The first entry whose condition evaluates to true wins
- An entry without
ifis a fallback (always matches) - If no entries match, the submission is treated as terminal and the workflow completes in place
JMESPath Expressions
JMESPath is the default expression language for conditions. Common patterns:
// String comparison
"if": "contact_time == 'morning'"
// Logical OR
"if": "contact_time == 'morning' || contact_time == 'afternoon'"
// Logical AND
"if": "is_vip && has_consent"
// Negation
"if": "!opted_out"
// Numeric comparison (note backticks for literals)
"if": "local.attempts >= `3`"
Important: In JMESPath, boolean and numeric literals require backticks:
- `
flag ==true(notflag == true`) - `
count >=3(notcount >= 3`)
Variable Access in Conditions
After on.submit actions execute, you can access:
| Variable | Access Pattern |
|---|---|
| Saved global variables | contact_time (directly by name) |
| Task-local variables | local.steps_completed |
| Current step inputs | inputs.contact_time |
The save action runs before transition evaluation, so contact_time is available for routing decisions.
Fallback Routes
Always include a fallback route (no if condition) as the last entry:
"next": [
{"if": "condition1", "id": "STEP_A"},
{"if": "condition2", "id": "STEP_B"},
{"id": "DEFAULT_STEP"} // Catches everything else
]
This prevents the workflow from getting stuck if none of the conditions match.
Multiple Terminal Steps
This example has two terminal steps (SCHEDULE_CALL and SEND_EMAIL), each with:
- Unique instructions tailored to that path
- No inputs (confirmation only)
- Empty
next: [](terminal)
The user's journey ends differently based on their preference:
User prefers morning → SCHEDULE_CALL → "We'll call you during business hours"
User prefers evening → SEND_EMAIL → "We'll send you an email"
How It Works
Path A: Morning/Afternoon Preference
User: "Hello"
Agent: "Welcome! Step 1 of 3. What is your name?"
User: "Alice"
Agent: "Great! Step 2 of 3. What is your email address?"
User: "alice@example.com"
Agent: "Almost done! Step 3 of 3. What is your preferred contact time?"
User: "morning"
[Agent calls submit_contact_info(contact_time="morning")]
→ on.submit: save contact_time
→ Evaluate next:
- "morning == 'morning' || morning == 'afternoon'" → TRUE
→ Transition to SCHEDULE_CALL
Agent: "Thank you! We'll call you during business hours."
[Agent completes SCHEDULE_CALL]
→ Workflow ends
Path B: Evening/Night Preference
... (same first 3 steps) ...
User: "evening"
[Agent calls submit_contact_info(contact_time="evening")]
→ on.submit: save contact_time
→ Evaluate next:
- "evening == 'morning' || evening == 'afternoon'" → FALSE
- "evening == 'evening' || evening == 'night'" → TRUE
→ Transition to SEND_EMAIL
Agent: "Thank you! We'll send you an email since you prefer evening contact."
[Agent completes SEND_EMAIL]
→ Workflow ends
Workflow Structure
┌─────────────────┐
│ COLLECT_NAME │
└────────┬────────┘
│
┌────────▼────────┐
│ COLLECT_EMAIL │
└────────┬────────┘
│
┌────────▼────────┐
│ COLLECT_TIME │
└────────┬────────┘
│
┌─────────────────┴─────────────────┐
│ │
morning/afternoon evening/night
│ │
┌──────▼──────┐ ┌───────▼───────┐
│ SCHEDULE_ │ │ SEND_EMAIL │
│ CALL │ │ │
└─────────────┘ └───────────────┘
(terminal) (terminal)
Try It
To test this workflow in the Syllable Console:
- Create a new tool with the JSON above
- Assign it to an agent
- Test both paths:
- Say "morning" as your preferred time → expect phone call confirmation
- Say "evening" as your preferred time → expect email confirmation
Verify the routing: Each path should deliver different confirmation messages based on the user's preference.
What's Next
This example routes based on valid input, but what if the user provides invalid data? In Example 6: Retry Loop, you'll learn how to:
- Validate email format before proceeding
- Allow retry attempts when validation fails
- Use counters to limit retries
- Loop back to the same step on failure

