Asterisk

Asterisk Dialplan Basics: Contexts, Extensions, Priorities and Pattern Matching

Himanshu Pal

Himanshu Pal

The dialplan is the PBX

Everything Asterisk does with a call — where it routes, what it plays, who it rings, what it refuses — is decided by the dialplan. It lives in extensions.conf, conventionally in /etc/asterisk, and it is built from exactly three concepts.

Contexts, extensions, priorities

A context is a named group of extensions, written in square brackets. Every channel entering the dialplan starts in a specific context, and it can only reach extensions in that context or in contexts explicitly included by it.

An extension is what the caller dialled — a number, a name, or a pattern that matches a range of numbers.

A priority is a step in the sequence of actions for that extension. Priorities run in order: 1, then 2, then 3.

[from-internal]
exten => 100,1,Answer()
 same => n,Playback(hello-world)
 same => n,Hangup()

Two shortcuts appear there and both are worth adopting. same => avoids repeating the extension name on every line — available since the Asterisk 1.6.2 branch. And n means "next priority", so you do not renumber every line when you insert a step in the middle. Hard-coded priority numbers are a maintenance trap; use n.

Contexts are a security boundary

This is the part that matters most and gets treated as an afterthought. A context does not merely organise your dialplan — it defines what a caller is permitted to do.

Put your international dialling rules in the same context that inbound calls from the internet land in, and you have handed the world a free international trunk. This is a leading cause of toll fraud, and it is a configuration mistake rather than a break-in.

The safe structure is layered by privilege:

[from-external]          ; untrusted: inbound calls only
exten => _X.,1,Goto(inbound-routing,${EXTEN},1)

[internal-local]         ; internal and local calls
exten => _1XX,1,Dial(PJSIP/${EXTEN},20)

[internal-national]      ; adds national dialling
include => internal-local
exten => _0NXXXXXXXXX,1,Dial(PJSIP/trunk/${EXTEN})

[internal-international] ; adds international
include => internal-national
exten => _00.,1,Dial(PJSIP/trunk/${EXTEN})

Now an extension's permissions are decided purely by which context it is assigned to. A phone in internal-local cannot dial internationally no matter what its user types, because those rules do not exist in its context. That is a far stronger control than hoping nobody guesses a prefix.

Pattern matching

Writing an extension for every possible number is impossible, so patterns match ranges. Every pattern begins with an underscore — that is how Asterisk knows it is a pattern rather than an extension with an unusual name. Forgetting the underscore is the single most common dialplan mistake, and it fails silently: the extension simply never matches.

The characters:

  • X — a single digit 0 to 9
  • Z — a single digit 1 to 9 (excludes zero)
  • N — a single digit 2 to 9
  • [1-5] — a single digit from the given range or set
  • . — one or more remaining characters, for extensions of indeterminate length
  • ! — zero or more remaining characters, used in overlap dialling

Some practical patterns:

_1XX          ; 100 through 199 — three-digit extensions
_0NXXXXXXXXX  ; national numbers starting 0, second digit 2-9
_00.          ; anything beginning 00 — international
_[89]XX       ; 800-899 and 900-999

Which pattern wins

When several patterns could match, Asterisk chooses the most specific, not the first one written. Specificity is decided by how many possibilities each character permits — so N (eight possible digits) sorts before X (ten), because it is narrower.

This is why an exact extension always beats a pattern, and why _1XX beats _XXX. It also means the order of lines in the file does not determine matching, which surprises people arriving from systems where it does.

Be careful with the dot

_. on its own matches almost everything, including internal feature codes and special extensions like i, t and h. It will swallow calls you did not intend it to. Prefer an anchored pattern such as _00. or _9. that requires a specific prefix first.

Special extensions

A few extension names have defined meanings rather than being dialled:

  • s — the start extension, used when a call arrives with no specific destination
  • i — invalid: the caller dialled something with no match
  • t — timeout: the caller stopped dialling
  • h — hangup: runs after the call ends, useful for cleanup and logging

Providing i and t in any context that collects digits is what separates a polished IVR from one that hangs up silently on a mistyped entry.

Testing before you break something

The dialplan is live configuration, and a mistake routes real calls wrongly. Reload and inspect rather than dialling hopefully:

asterisk -rx "dialplan reload"
asterisk -rx "dialplan show from-internal"
asterisk -rx "dialplan show 5551234@from-internal"

That last form is the useful one — it shows exactly which extension a given number would match in a given context, which settles pattern-precedence arguments instantly.

Frequently asked questions

Why does my pattern not match?

Most often a missing leading underscore. Without it Asterisk treats the line as a literal extension name, and it silently never matches.

What is the difference between X, Z and N?

X matches 0–9, Z matches 1–9, N matches 2–9. Choosing the narrowest one that fits both prevents unintended matches and makes precedence predictable.

Which pattern takes priority?

The most specific, not the first in the file. Fewer possible matches means higher precedence, so N outranks X and an exact extension outranks any pattern.

Why should each extension have its own context?

Because contexts control what a caller may dial. Assigning phones to contexts by privilege level is how you stop an internal extension — or a compromised one — placing international calls.