Follow-Me via Local Channels
Follow-Me via Local Channels
When a call goes unanswered at desk phones, you often want to try cell phones next. This snippet uses Local channels to dial external numbers through a separate 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., which lets you control the outbound caller IDThe calling party number (and optionally name) presented on an outbound call, set from the endpoint or manipulated in the dialplan. independently for each leg. The important part is preserving the original inbound DID as the outbound CID so the cell phone user sees who's actually calling.
The Snippet
The subroutine that rings all cell phones simultaneously:
[followme-all]
exten => s,1,NoOp(Follow Me All - Ringing all cell phones)
; Build dial string using Local channels
; Each Local channel routes through followme-external context
same => n,Set(CELL_DIAL_STRING=Local/CELL_100@followme-external&Local/CELL_101@followme-external&Local/CELL_102@followme-external)
same => n,Dial(${CELL_DIAL_STRING},30,tT)
same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?answered)
same => n,Set(GOSUB_RETVAL=NOANSWER)
same => n,Return()
same => n(answered),Set(GOSUB_RETVAL=ANSWERED)
same => n,Return()
The external dialing context that handles CID and trunkA connection between Asterisk and another PBX or an ITSP/carrier, used to send and receive external calls. routing:
[followme-external]
; Extension 100's cell phone
exten => CELL_100,1,NoOp(Follow Me to cell - ext 100)
same => n,Set(CALLERID(num)=${IF($["${INCOMING_DID}" != ""]?${INCOMING_DID}:${MAIN_DID})})
same => n,Dial(PJSIP/18025551234@${TRUNK},30,tT)
same => n,Hangup()
; Extension 101's cell phone
exten => CELL_101,1,NoOp(Follow Me to cell - ext 101)
same => n,Set(CALLERID(num)=${IF($["${INCOMING_DID}" != ""]?${INCOMING_DID}:${MAIN_DID})})
same => n,Dial(PJSIP/16035559876@${TRUNK},30,tT)
same => n,Hangup()
; Extension 102's cell phone
exten => CELL_102,1,NoOp(Follow Me to cell - ext 102)
same => n,Set(CALLERID(num)=${IF($["${INCOMING_DID}" != ""]?${INCOMING_DID}:${MAIN_DID})})
same => n,Dial(PJSIP/17815554321@${TRUNK},30,tT)
same => n,Hangup()
The inbound context sets __INCOMING_DID (double underscore) so it inherits through Local channelA single call leg passing through Asterisk. Channels represent connections to endpoints and are what dialplan applications act on. hops:
[from-trunk]
exten => 16035551000,1,NoOp(Inbound call from ${CALLERID(num)})
same => n,Set(__INCOMING_DID=${EXTEN})
same => n,GotoIfTime(08:00-17:00,mon-fri,*,*?ring-group,s,1)
same => n,Goto(afterhours,s,1)
How It Works
Local channels create a new channel pair inside Asterisk that connects two 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 → contexts. When you Dial(Local/CELL_100@followme-external), Asterisk creates a mini-call that starts executing at CELL_100 in the followme-external context. This gives you a separate 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 → execution environment where you can set CID, apply different logic, or route through a specific trunk.
Double-underscore inheritance (__INCOMING_DID): Variables prefixed with __ inherit across channel boundaries, including Local channel hops. A single underscore _ only inherits one hop deep. Since the call flow is: inbound trunk -> Local channel -> outbound trunk, we need __ (two hops).
CID preservation: The followme-external context sets CALLERID(num) to the original inbound DID. This means when the cell phone rings, the caller ID shows the company's number, not the PBXPrivate Branch Exchange, a private telephone switch. Asterisk is a software PBX that routes calls between internal extensions and the outside world.'s trunk number. If no inbound DID is set (e.g., internal call), it falls back to MAIN_DID.
Why Local Channels Instead of app_followme
app_followme (the FollowMe() application with followme.conf) works well for simple cases. Local channels give you more control:
- Per-leg CID: Each cell phone can get a different outbound CID
- Per-leg trunk: You could route different numbers through different trunks
- Simultaneous ring: All cell phones ring at once (app_followme rings them sequentially by default)
- Custom logic: You can add any dialplan logic before the external dial (time checks, DND for cell phones, etc.)
Calling from After-Hours Context
[afterhours]
exten => s,1,Answer()
same => n,Wait(1)
same => n,Playback(after-hours-greeting)
; Try desk phones first
same => n,Dial(PJSIP/100&PJSIP/101&PJSIP/102,20,tTr)
same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?done)
; No answer at desk - try cell phones
same => n,Gosub(followme-all,s,1)
same => n,GotoIf($["${GOSUB_RETVAL}" = "ANSWERED"]?done)
; Still no answer - voicemail
same => n,VoiceMail(100@default,u)
same => n(done),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.