Dial Plans
Build visual call routing flows with dial plans.
Overview
A dial plan defines how incoming calls are routed through a series of steps, represented as a flowchart. Each step in the flowchart is called a node. Nodes check conditions (like business hours) or ring extensions, then route to the next step based on the outcome.
Think of it like a flowchart you'd draw on a whiteboard:
┌───────────┐
│ Start │
└─────┬─────┘
│
▼
┌─────────────┐ open ┌───────────────┐
│ Check Hours │────────────▶│ Reception │
│ [Schedule] │ │ [InternalDial]│
└──────┬──────┘ └───────┬───────┘
│ │
│ closed timeout
│ │
└───────────┬────────────────┘
│
▼
┌───────────┐
│ Voicemail │
└───────────┘
In this example:
- Every call starts at the "Check Hours" schedule node
- If the schedule is open, the call rings the reception extension
- If reception doesn't answer (timeout), or if the schedule is closed, the call routes to voicemail
Node Types
Schedule Node
Routes calls based on whether a schedule is currently open, closed, or on holiday.
Exits:
open— Schedule is currently within business hoursclosed— Schedule is outside business hoursholiday— Current date falls within a holiday range
Internal Dial Node
Dials a user or nested dial plan and routes based on the outcome.
Configuration:
target_id— ID of the user, ring group, or dial plan to dialtimeout— How long to ring before giving up (0-300 seconds, default 30)
Exits:
next— Ring timed out or user was busy (call continues to next node)- (answered) — If answered, the dial plan ends and the call is connected
Creating a Dial Plan
- SDK
- cURL
const dialPlan = await dialstack.dialPlans.create(
{
name: 'Main Line Routing',
entry_node: 'check_hours',
nodes: [
{
id: 'check_hours',
type: 'schedule',
config: {
schedule_id: 'sched_01h2xcejqtf2nbrexx3vqjhp50',
open: 'reception',
closed: 'voicemail',
holiday: 'voicemail',
},
},
{
id: 'reception',
type: 'internal_dial',
config: {
target_id: 'user_01h2xcejqtf2nbrexx3vqjhp45',
timeout: 30,
next: 'voicemail',
},
},
{
id: 'voicemail',
type: 'internal_dial',
config: {
target_id: 'user_01h2xcejqtf2nbrexx3vqjhp99',
},
},
],
},
{ dialstackAccount: 'acct_01h2xcejqtf2nbrexx3vqjhp41' }
);
curl -X POST https://api.dialstack.ai/v1/dial_plans \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "DialStack-Account: acct_01h2xcejqtf2nbrexx3vqjhp41" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Line Routing",
"entry_node": "check_hours",
"nodes": [
{
"id": "check_hours",
"type": "schedule",
"config": {
"schedule_id": "sched_01h2xcejqtf2nbrexx3vqjhp50",
"open": "reception",
"closed": "voicemail",
"holiday": "voicemail"
}
},
{
"id": "reception",
"type": "internal_dial",
"config": {
"target_id": "user_01h2xcejqtf2nbrexx3vqjhp45",
"timeout": 30,
"next": "voicemail"
}
},
{
"id": "voicemail",
"type": "internal_dial",
"config": {
"target_id": "user_01h2xcejqtf2nbrexx3vqjhp99"
}
}
]
}'
Key Concepts
Entry Node
The entry_node specifies which node executes first. Think of it as where the "Start" arrow points in your flowchart.
Exit Targets
Each node type has specific exits that route to other nodes. Set an exit to null (or omit it) to terminate the call at that point.
Node IDs
Node IDs are strings you define (like "check_hours" or "reception"). They must be unique within a dial plan and are used to wire nodes together.
Visual Position
Nodes can include optional position coordinates (x, y) for rendering in a visual editor. If omitted, nodes are auto-positioned.
Examples
After-Hours Voicemail
Route calls to voicemail when closed:
Start → Check Hours [Schedule] → (open) → Ring Group [Internal Dial]
→ (closed/holiday) → Voicemail
{
"name": "After-Hours Voicemail",
"entry_node": "check_hours",
"nodes": [
{
"id": "check_hours",
"type": "schedule",
"config": {
"schedule_id": "sched_...",
"open": "ring_group",
"closed": "voicemail",
"holiday": "voicemail"
}
},
{
"id": "ring_group",
"type": "internal_dial",
"config": {
"target_id": "user_...",
"next": "voicemail"
}
},
{
"id": "voicemail",
"type": "internal_dial",
"config": {
"target_id": "user_..."
}
}
]
}
Multi-Level Routing
Chain multiple schedule checks:
Start → Check Emergency [Schedule] → (open) → Emergency Line [Internal Dial]
→ (closed/holiday) → Check Hours [Schedule] → (open) → Reception [Internal Dial]
→ (closed/holiday) → Voicemail
{
"name": "Multi-Level Routing",
"entry_node": "check_emergency",
"nodes": [
{
"id": "check_emergency",
"type": "schedule",
"config": {
"schedule_id": "sched_emergency_...",
"open": "emergency_line",
"closed": "check_hours",
"holiday": "check_hours"
}
},
{
"id": "emergency_line",
"type": "internal_dial",
"config": {
"target_id": "user_emergency_..."
}
},
{
"id": "check_hours",
"type": "schedule",
"config": {
"schedule_id": "sched_business_...",
"open": "reception",
"closed": "voicemail",
"holiday": "voicemail"
}
},
{
"id": "reception",
"type": "internal_dial",
"config": {
"target_id": "user_reception_...",
"next": "voicemail"
}
},
{
"id": "voicemail",
"type": "internal_dial",
"config": {
"target_id": "user_voicemail_..."
}
}
]
}
Fallback Chain
Try multiple users before voicemail:
Start → Reception [Internal Dial] → (timeout 30s) → Manager [Internal Dial] → (timeout 20s) → Voicemail
{
"name": "Fallback Chain",
"entry_node": "reception",
"nodes": [
{
"id": "reception",
"type": "internal_dial",
"config": {
"target_id": "user_reception_...",
"timeout": 30,
"next": "manager"
}
},
{
"id": "manager",
"type": "internal_dial",
"config": {
"target_id": "user_manager_...",
"timeout": 20,
"next": "voicemail"
}
},
{
"id": "voicemail",
"type": "internal_dial",
"config": {
"target_id": "user_voicemail_..."
}
}
]
}
API Reference
- Dial Plans — Create, update, and delete dial plans
- Schedules — Define business hours for schedule nodes