Reusable Dial-User Subroutine
Reusable Dial-User Subroutine
Instead of duplicating DND checks, call forward logic, and voicemailAsterisk's built-in voice messaging system (app_voicemail), configured in voicemail.conf with mailboxes per user. fallback in every extensionA dialplan entry that matches a dialed number or pattern within a context and triggers a sequence of prioritized steps. definition, this pattern centralizes all per-user call handling into a single Gosub. Every extension calls the same subroutine, passing just the extension number and display name.
The Snippet
The subroutine itself:
[dial-user]
exten => s,1,NoOp(Dialing ${ARG1} - ${ARG2})
; Check DND (stored in AstDB)
same => n,Set(DND=${DB(DND/${ARG1})})
same => n,GotoIf($["${DND}" = "yes"]?dnd)
;
; Check Call Forward
same => n,Set(CF=${DB(CF/${ARG1})})
same => n,GotoIf($["${CF}" != ""]?forward)
;
; Check Follow Me
same => n,Set(FM=${DB(FM/${ARG1})})
same => n,GotoIf($["${FM}" = "yes"]?followme)
;
; Normal dial
same => n,Dial(PJSIP/${ARG1},30,tTxX)
same => n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy)
same => n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?noanswer)
same => n,GotoIf($["${DIALSTATUS}" = "CHANUNAVAIL"]?unavail)
same => n,Return()
;
same => n(dnd),VoiceMail(${ARG1}@default,u)
same => n,Return()
;
same => n(forward),NoOp(Call Forward to ${CF})
; Internal extension (pattern match) - dial directly
same => n,GotoIf($["${CF}" : "^[0-9][0-9][0-9][0-9]?$"]?fwd_internal)
; Matches 3-4 digit internal extensions; adjust pattern to match your range
; External number - route through trunk
same => n,Dial(PJSIP/1${CF}@${TRUNK},120,tT)
same => n,VoiceMail(${ARG1}@default,u)
same => n,Return()
same => n(fwd_internal),Dial(PJSIP/${CF},30,tT)
same => n,VoiceMail(${ARG1}@default,u)
same => n,Return()
;
same => n(followme),FollowMe(${ARG1},ds)
same => n,GotoIf($["${FMSTATUS}" = "SUCCESS"]?:noanswer)
same => n,Return()
;
same => n(busy),VoiceMail(${ARG1}@default,b)
same => n,Return()
;
same => n(noanswer),VoiceMail(${ARG1}@default,u)
same => n,Return()
;
same => n(unavail),VoiceMail(${ARG1}@default,u)
same => n,Return()
Calling the subroutine from your extension definitions:
[dial-extension]
exten => 100,1,NoOp(Dial Alice)
same => n,Gosub(dial-user,s,1(100,Alice))
same => n,Hangup()
exten => 101,1,NoOp(Dial Bob)
same => n,Gosub(dial-user,s,1(101,Bob))
same => n,Hangup()
exten => 102,1,NoOp(Dial Carol)
same => n,Gosub(dial-user,s,1(102,Carol))
same => n,Hangup()
; Fallback pattern for any extension in range
exten => _1XX,1,NoOp(Dial extension ${EXTEN})
same => n,Dial(PJSIP/${EXTEN},30,tT)
same => n,VoiceMail(${EXTEN}@default,u)
same => n,Hangup()
How It Works
The subroutine checks features in priority order:
- DND: if
DB(DND/100)isyes, go straight to voicemail. No ring, no disturbance. - Call Forward: if
DB(CF/100)is set to a number, dial that number instead. Handles both internal (direct PJSIPThe modern SIP channel driver in Asterisk (chan_pjsip), replacing the older chan_sip. Configured in pjsip.conf using endpoints, AORs, auths, and transports. dial) and external (route through trunkA connection between Asterisk and another PBX or an ITSP/carrier, used to send and receive external calls.) forwarding. - Follow Me: if
DB(FM/100)isyes, useapp_followmeto try the user's configured follow-me numbers (defined infollowme.conf). - Normal Dial: ring the PJSIP endpointIn PJSIP configuration, the SIP entity Asterisk communicates with: a phone, trunk, or other user agent. Defined by an endpoint section in pjsip.conf. for 30 seconds with transfer enabled (
tT) and recording enabled (xX). - DIALSTATUS routing: after Dial(), route to appropriate voicemail greeting (busy vs unavailable).
Why This Pattern Matters
Without this subroutine, every extension in your 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 → would need its own DND check, CF check, FM check, and DIALSTATUS handling. For a 50-extension system, that's thousands of duplicated lines. With this pattern, adding a new extension is one Gosub call.
The feature state lives in AstDB, which means users can toggle features from their phones using feature codes (*78/*79 for DND, *72/*73 for CF, *21/*22 for Follow Me) without any 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 → reload.
Companion Feature Codes
These feature codes set the AstDB values that the subroutine checks:
[features]
; *78/*79 - DND Enable/Disable
exten => *78,1,Set(DB(DND/${CALLERID(num)})=yes)
same => n,Playback(beep)
same => n,Hangup()
exten => *79,1,Set(DB_DELETE(DND/${CALLERID(num)})=)
same => n,Playback(beep)
same => n,Hangup()
; *72/*73 - Call Forward Enable/Disable
exten => *72,1,Read(FWD_NUM,vm-enter-num-to-call,15)
same => n,GotoIf($["${FWD_NUM}" = ""]?cancel)
same => n,Set(DB(CF/${CALLERID(num)})=${FWD_NUM})
same => n,Playback(beep)
same => n,SayDigits(${FWD_NUM})
same => n,Hangup()
same => n(cancel),Playback(cancelled)
same => n,Hangup()
exten => *73,1,Set(DB_DELETE(CF/${CALLERID(num)})=)
same => n,Playback(beep)
same => n,Hangup()
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.