Transitions define when and how the conversation moves from one node to another. Each node can have multiple transitions; they are evaluated by priority (highest first) and the first matching transition fires.
Transition Types
| Type | Evaluation | Use Case |
|---|
| Natural Language | LLM-based | Intent detection, sentiment |
| Structured Equation | Rule-based | Variable comparisons, thresholds |
| DTMF | Keypad input | IVR menus, quick selection |
| Always | No condition | Fallback / default path |
Priority
Each transition has a numeric priority. Higher values are evaluated first. When multiple transitions have the same priority, their order is indeterminate.
Default priorities if not set manually:
- Natural Language: 100
- Structured Equation: 200
- DTMF: 300
- Always: 0
Set priorities explicitly when you need a specific evaluation order.
Always include an Always transition as a fallback. If no transitions match, the conversation stalls.
Natural Language Transitions
The LLM evaluates whether the condition (prompt) is met based on the user’s input and conversation context.
Configuration
{
type: 'natural_language',
prompt: string, // Condition description (required)
description?: string
}
Examples
Prompt: "The user wants to speak with a human agent"
→ Target: Transfer_to_Agent
Prompt: "The user is asking about billing or payment issues"
→ Target: Billing_Department
Prompt: "The user agrees or says yes"
→ Target: Confirm_Path
Prompt: "The user declines or says no"
→ Target: Alternative_Path
Write conditions that describe user intent, not exact phrases. “The user wants to speak with a human” matches “get me an agent”, “transfer me”, “I need a person”, etc.
Structured Equation Transitions
Equation transitions evaluate variable conditions using rule-based logic — no LLM involved.
Configuration
{
type: 'structured_equation',
logic: 'all' | 'any', // AND or OR
conditions: Array<{
variable: string,
operator: Operator,
value: string | number | boolean,
description?: string
}>,
description?: string
}
Operators
| Operator | Description |
|---|
equals | Exact match |
not_equals | Not equal |
greater_than | Numeric |
less_than | Numeric |
greater_than_or_equal | Numeric |
less_than_or_equal | Numeric |
contains | String contains substring |
not_contains | String does not contain |
exists | Variable has any value |
not_exists | Variable is null/undefined |
Examples
Single condition:
Variable: account_status
Operator: equals
Value: 'active'
→ Target: Active_Account_Path
AND logic (all conditions must pass):
Logic: all
Conditions:
- account_balance > 1000
- account_type equals "premium"
→ Target: VIP_Path
OR logic (any condition passes):
Logic: any
Conditions:
- support_tier equals "platinum"
- is_enterprise equals true
→ Target: Priority_Support
Existence check:
Variable: customer_id
Operator: exists
→ Target: Known_Customer_Path
Hamsa handles type coercion automatically — a string "25" compared to number 21 with greater_than evaluates correctly.
DTMF Transitions
DTMF transitions fire when the user presses a specific keypad key.
→ See DTMF Features for full documentation on all DTMF capabilities.
Configuration
{
type: 'dtmf',
key: '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '*' | '#',
description?: string
}
Example
Conversation Node: "Main Menu"
Message: "Press 1 for Sales, Press 2 for Support, Press 3 for Billing"
Transitions:
- DTMF: key=1 → Sales_Department
- DTMF: key=2 → Support_Department
- DTMF: key=3 → Billing_Department
- Always → Repeat_Menu
Always Transitions
Always transitions have no condition — they fire when no other transition matches. Use them as the fallback on every node that has conditional transitions.
Configuration
{
type: 'always',
description: string
}
Examples
Menu fallback:
Transitions:
- DTMF: key=1 → Sales
- DTMF: key=2 → Support
- Always → Repeat_Menu # User didn't press a valid key
Router else path:
Transitions:
- Equation: account_type equals "premium" → Premium_Flow
- Equation: account_type equals "standard" → Standard_Flow
- Always → Basic_Flow
Always transitions should have the lowest priority (default 0) so they are evaluated last.
Advanced Patterns
Combining types
Node: "Support Menu"
Message: "Describe your issue, or press 0 for an agent"
Transitions:
- DTMF: key=0 → Transfer_Agent (priority: 1000)
- Natural: "urgent issue" → Urgent (priority: 200)
- Natural: "billing" → Billing (priority: 200)
- Always → General_Inquiry (priority: 0)
Loops with exit conditions
Node: "Collect Account Number"
Message: "Please provide your 8-digit account number"
Transitions:
- Equation: account_number.length equals 8 → Proceed (priority: 200)
- Natural: "speak to agent" → Transfer (priority: 150)
- Equation: attempt_count >= 3 → Max_Attempts (priority: 100)
- Always → Retry_Input (priority: 0)
Time-based routing
Router Node: "Hours Check"
Transitions:
- Logic: all
Conditions:
- current_hour >= 9
- current_hour < 17
Target: Business_Hours_Flow (priority: 200)
- Always → After_Hours_Flow (priority: 0)
Validation
The flow builder validates transitions before deployment:
- All transitions have non-empty conditions
- Target nodes exist
- Every non-terminal node has at least one transition
- DTMF keys are valid (0–9, *, #)
- Warning if no Always transition (routing may stall)
Transition Schema
interface Transition {
id: string;
name?: string;
condition:
| NaturalLanguageCondition
| StructuredEquationCondition
| DTMFCondition
| AlwaysCondition;
targetNodeId: string;
priority: number;
isEnabled: boolean;
}
Next Steps
Global Nodes
Nodes reachable from anywhere in the flow
DTMF Features
Full keypad interaction documentation
Variables
Variables used in equation conditions
Router Node
Pure routing without conversation