Follow-Me via Local Channels

Call Handling Asterisk 20+ -- Last reviewed 2026-08-22 call-handling follow-me local-channels gosub callerid Found this useful? Upvote it. ×

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:

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.

Moderated before publishing. Email never shown.
Related Snippets