Dialplan Contexts, Extensions, and Priorities

Getting Started -- Last reviewed 2026-06-13 dialplan beginner getting-started contexts extensions Found this useful? Upvote it. ×

Dialplan Contexts, Extensions, and Priorities

Asterisk's dialplan looks like a config file, but it runs like a script. Calls come in, hit a matching extension, and execute a sequence of steps. Three concepts hold the whole thing together: contexts, extensions, and priorities.

On this page

Contexts

A context is a named block in the dialplan. 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 PJSIP endpoint 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 trunk. 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 voicemail 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.

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.

Moderated before publishing. Email never shown.
Related Snippets