Server

Your First Asterisk Dialplan

By Prabath Thalangama· October 24, 2026· 4 min read
#asterisk#voip#dialplan

Introduction

Asterisk routes calls by executing a dialplan — priority-ordered steps grouped into contexts. Endpoints (phones, trunks) are defined separately (these days in pjsip.conf). This is a minimal but real setup.

Endpoints (pjsip.conf)

[transport-udp]
type = transport
protocol = udp
bind = 0.0.0.0:5060

; --- an internal phone, extension 1001 ---
[1001]
type = endpoint
context = internal
disallow = all
allow = ulaw,alaw
auth = 1001-auth
aors = 1001

[1001-auth]
type = auth
auth_type = userpass
username = 1001
password = a-strong-secret

[1001]
type = aor
max_contacts = 1

; --- a SIP trunk to an ITSP ---
[trunk]
type = endpoint
context = from-trunk
disallow = all
allow = ulaw,alaw
outbound_auth = trunk-auth
aors = trunk
from_user = 0111234567
from_domain = sip.itsp.example

[trunk-auth]
type = auth
auth_type = userpass
username = 0111234567
password = trunk-secret

[trunk]
type = aor
contact = sip:sip.itsp.example:5060

[trunk-identify]
type = identify
endpoint = trunk
match = sip.itsp.example

Dialplan (extensions.conf)

[globals]
TRUNK = PJSIP/trunk

[internal]
; internal extensions 1000-1099
exten => _10XX,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
 same => n,Dial(PJSIP/${EXTEN},20)
 same => n,Goto(voicemail,${EXTEN},1)

; voicemail retrieval
exten => *97,1,VoiceMailMain(${CALLERID(num)}@default)

; outbound: 0 + national number, or 00 + international
exten => _0.,1,NoOp(Outbound ${EXTEN})
 same => n,Set(CALLERID(num)=0111234567)
 same => n,Dial(${TRUNK}/${EXTEN},60)
 same => n,Hangup()

; the IVR / main menu
exten => 100,1,Answer()
 same => n,Background(custom/welcome)     ; "Press 1 for sales, 2 for support"
 same => n,WaitExten(5)
exten => 1,1,Dial(PJSIP/1001,20)
exten => 2,1,Dial(PJSIP/1002,20)
exten => i,1,Playback(pbx-invalid)        ; invalid input
 same => n,Goto(100,1)
exten => t,1,Playback(vm-goodbye)         ; timeout
 same => n,Hangup()

[voicemail]
exten => _10XX,1,VoiceMail(${EXTEN}@default,u)   ; u = play "unavailable" greeting
 same => n,Hangup()

[from-trunk]
; inbound DID -> IVR
exten => 0111234567,1,Goto(internal,100,1)
exten => _X.,1,Goto(internal,100,1)

The concepts

  • Context ([internal]) — a namespace. An endpoint's context= is where its calls start. This is your security boundary: from-trunk must not be able to reach the outbound-dial extensions, or you have toll fraud.
  • Extension (exten =>) — a dialled string or pattern. Patterns: _ prefix, X=0-9, Z=1-9, N=2-9, .=one-or-more, !=zero-or-more. _10XX matches 1000–1099.
  • Priority — steps run in order: 1, then n (next), then n again. Labels: same => n(retry),... + Goto(retry).
  • Application — Dial, Playback, Background, VoiceMail, Hangup, Set, Goto, NoOp. Dial(PJSIP/1001,20) rings for 20s.
  • Special extensions: i (invalid), t (response timeout), s (start, when no extension was given), h (hangup handler), T (absolute timeout).

Reloading and testing

asterisk -rx "dialplan reload"
asterisk -rx "pjsip reload"
asterisk -rx "dialplan show internal"
asterisk -rx "pjsip show endpoints"

Verification and troubleshooting

asterisk -rvvvvv                     # console with verbosity
asterisk -rx "pjsip set logger on"   # SIP trace
asterisk -rx "core show channels"
asterisk -rx "dialplan show 1001@internal"
  • Phone won't register — pjsip show endpoint 1001 (check auth/aors linkage), wrong password, NAT (rewrite_contact/rtp_symmetric / external_media_address for the trunk), or 5060 blocked.
  • Call connects, no audio / one-way — RTP, not SIP. NAT: set external_media_address, external_signaling_address, local_net on the transport; open the RTP port range (rtp.conf).
  • "All circuits are busy" on outbound — trunk not registered / identify not matching, wrong context on the trunk endpoint, or the dialplan pattern didn't match (dialplan show the exact number).
  • Inbound DID rings nowhere — from-trunk context has no matching exten for the DID the ITSP sends (they may send it with/without country code, or as the SIP user). NoOp(${EXTEN}) to see what actually arrives.
  • Toll fraud risk — an endpoint or the from-trunk context can reach _0. outbound. Keep untrusted contexts free of trunk-dialing extensions; allowguest=no.
  • IVR: DTMF not detected — DTMF mode mismatch (dtmf_mode = rfc4733 on the endpoint), or Background vs Read; try Read() for reliable digit collection.
  • dialplan reload didn't apply — a syntax error aborted the parse; dialplan show still shows the old plan. Check the console for the error line.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.