Dialplan Contexts, Extensions, and Priorities
Dialplan Contexts, Extensions, and Priorities
Asterisk's dialplanThe core call-routing configuration of Asterisk, written mostly in extensions.conf as contexts, extensions, and priorities that decide how every call is handled. Full definition → looks like a config file, but it runs like a script. Calls come in, hit a matching extensionA dialplan entry that matches a dialed number or pattern within a context and triggers a sequence of prioritized steps., and execute a sequence of steps. Three concepts hold the whole thing together: contexts, extensions, and priorities.
On this page
Contexts
A contextA named section of the dialplan that groups extensions. Calls enter a specific context and can only reach extensions visible from it, making contexts the basic unit of call routing and security. is a named block in the dialplanThe core call-routing configuration of Asterisk, written mostly in extensions.conf as contexts, extensions, and priorities that decide how every call is handled. Full definition →. Square brackets, a name, and everything below it until the next context starts.
[internal]
exten => 100,1,Answer()
same => n,Playback(hello-world)
same => n,Hangup()
[from-trunk]
exten => s,1,Answer()
same => n,Playback(tt-weasels)
same => n,Hangup()
Why bother with separate blocks? Security. A phone in [internal] can only reach extensions defined there. It has no visibility into [from-trunk]. This matters because your internal context probably allows outbound dialing, and you absolutely do not want anonymous inbound callers to have access to that.
Contexts also keep things organized. As the dialplan grows, you end up with [internal], [from-trunk], [after-hours], [outbound], and so on. Each one handles a distinct part of the call flow.
Assigning phones to contexts
Each PJSIPThe modern SIP channel driver in Asterisk (chan_pjsip), replacing the older chan_sip. Configured in pjsip.conf using endpoints, AORs, auths, and transports. endpointIn PJSIP configuration, the SIP entity Asterisk communicates with: a phone, trunk, or other user agent. Defined by an endpoint section in pjsip.conf. has a context line that determines where its calls land:
[200]
type = endpoint
context = internal
Extension 200 dials a number, Asterisk looks inside [internal] for a match and nowhere else.
Jumping between contexts
Goto() moves a call from one context to another:
[internal]
exten => _9NXXNXXXXXX,1,Goto(outbound,${EXTEN},1)
[outbound]
exten => _9NXXNXXXXXX,1,Dial(PJSIP/trunk/${EXTEN:1})
User dials 9 followed by a phone number from [internal], the call lands in [outbound] where the pattern match strips the 9 and sends it out the trunkA connection between Asterisk and another PBX or an ITSP/carrier, used to send and receive external calls.. Keeps outbound routing logic in its own context instead of cluttering up the internal one.
The includes directive
You can also make one context's extensions available in another:
[internal]
include => features
[features]
exten => *98,1,VoicemailMain(${CALLERID(num)}@default)
Now phones in [internal] can dial *98 even though it lives in [features]. Useful for shared stuff like voicemailAsterisk's built-in voice messaging system (app_voicemail), configured in voicemail.conf with mailboxes per user. access, conference rooms, or parking lots.
One warning: be careful what you include where. If you include a context with outbound dialing into a context that handles inbound trunk calls, you have just created a toll fraud vulnerability.
Extensions
An extension matches a dialed number to a set of instructions. Asterisk compares what was dialed against each extension in the current context, finds the best match, and runs it.
[internal]
exten => 200,1,Dial(PJSIP/200,30)
exten => 201,1,Dial(PJSIP/201,30)
exten => 300,1,ConfBridge(main)
Pattern matching
You are not going to hardcode every possible phone number someone might dial. Asterisk has pattern matching for this, triggered by the _ prefix:
[outbound]
; Match 10-digit North American numbers
exten => _NXXNXXXXXX,1,Dial(PJSIP/trunk/${EXTEN})
; Match 11-digit with leading 1
exten => _1NXXNXXXXXX,1,Dial(PJSIP/trunk/${EXTEN})
; Match 3-digit internal extensions
exten => _2XX,1,Dial(PJSIP/${EXTEN},30)
The pattern characters:
| Character | Matches |
|---|---|
X |
Any digit 0-9 |
Z |
Any digit 1-9 |
N |
Any digit 2-9 |
[15-7] |
Digit 1, 5, 6, or 7 |
. |
One or more of any character (wildcard) |
! |
Wildcard; causes the matching process to complete as soon as it can unambiguously determine that no other matches are possible |
When multiple patterns could match, Asterisk picks the most specific one. An exact match like exten => 200 always beats a pattern like exten => _2XX.
Special extensions
A few extension names are reserved:
| Extension | Purpose |
|---|---|
s |
"Start." Incoming calls with no DID land here |
i |
"Invalid." Fires when the caller presses something that matches nothing |
t |
"Timeout." Fires when WaitExten() times out |
h |
"Hangup." Runs after the call disconnects, good for cleanup |
a |
"Assistant." When someone presses * inside VoicemailMain() |
If you are building an IVR and forget the i and t handlers, callers who press the wrong button or wait too long will hear nothing. Just dead air. Always define both.
Priorities
Priorities are the steps inside an extension. They run in order, top to bottom.
exten => 200,1,Answer()
exten => 200,2,Playback(hello-world)
exten => 200,3,Hangup()
The same => and n shorthand
Nobody manually numbers priorities anymore. If you insert a step in the middle, you have to renumber everything after it. Use same => n instead:
exten => 200,1,Answer()
same => n,Playback(hello-world)
same => n,Hangup()
same refers to the previous extension. n auto-increments the priority number. Start the first one at 1, then use n for everything after.
Labels
You can name a priority so you can jump to it later:
exten => 200,1,Answer()
same => n(greeting),Playback(welcome)
same => n,Goto(200,greeting)
The label greeting lets Goto() reference that step by name. If you rearrange priorities later, the label still works.
Conditional branching
GotoIf() is how the dialplan makes decisions:
exten => 200,1,Answer()
same => n,Set(OPEN=${IFTIME(09:00-17:00|mon-fri|*|*?1:0)})
same => n,GotoIf($[${OPEN}=1]?open:closed)
same => n(open),Playback(office-is-open)
same => n,Goto(reception,s,1)
same => n(closed),Playback(office-is-closed)
same => n,Voicemail(200@default,u)
same => n,Hangup()
The syntax is GotoIf(condition?true_label:false_label). Between labels, Goto(), and GotoIf(), the dialplan has enough control flow to handle most routing logic without external scripts.
A working example
A small-office dialplan tying all of this together:
[internal]
include => features
; Internal extensions
exten => _2XX,1,Dial(PJSIP/${EXTEN},30)
same => n,Voicemail(${EXTEN}@default,u)
same => n,Hangup()
; Outbound via 9 prefix
exten => _9NXXNXXXXXX,1,Set(CALLERID(num)=5551234567)
same => n,Dial(PJSIP/trunk/${EXTEN:1})
same => n,Hangup()
[features]
; Voicemail access
exten => *98,1,VoicemailMain(${CALLERID(num)}@default)
same => n,Hangup()
; Echo test
exten => *99,1,Answer()
same => n,Echo()
same => n,Hangup()
[from-trunk]
; Inbound calls from SIP provider
exten => s,1,Answer()
same => n,Background(press-1-for-sales&press-2-for-support)
same => n,WaitExten(5)
exten => 1,1,Dial(PJSIP/200&PJSIP/201,30)
same => n,Voicemail(200@default,u)
same => n,Hangup()
exten => 2,1,Dial(PJSIP/202,30)
same => n,Voicemail(202@default,u)
same => n,Hangup()
exten => i,1,Playback(invalid)
same => n,Goto(s,1)
exten => t,1,Dial(PJSIP/200,30)
same => n,Voicemail(200@default,u)
same => n,Hangup()
The two main contexts are isolated from each other. [internal] is for employee phones with outbound access. [from-trunk] handles outside callers and has its own IVR with i and t handlers so nobody gets stuck in silence. Shared features like voicemail live in their own context and get pulled in with include.
_2XX matches any three-digit extension starting with 2. _9NXXNXXXXXX matches 9 followed by a 10-digit number, and ${EXTEN:1} strips the leading 9 before sending to the trunk.
For Voicemail() and VoicemailMain() to work, matching mailboxes must exist in voicemail.conf. After editing extensions.conf or voicemail.conf, reload the dialplan from the CLI:
asterisk -rx "dialplan reload"
asterisk -rx "voicemail reload"
Debugging
When calls do the wrong thing, or nothing at all, connect to the CLI:
asterisk -rvvv
Make a call and watch. Asterisk prints each priority as it runs. You can also dump the parsed dialplan for a specific context:
asterisk -rx "dialplan show internal"
If an extension you wrote is missing from that output, either it is in a different context or it has a syntax error that prevented loading.
What to read next
- Dialplan Applications reference
- Dialplan Functions reference
- The
Goto(),GotoIf(),Gosub(), andDial()reference entries
User Notes
Know a tip or gotcha for this topic? Share it below and help others.
Contribute a note
Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.