After-Hours Escalation Chain
After-Hours Escalation Chain
When a call arrives outside business hours, don't just dump it to voicemail. This pattern plays a greeting, tries desk phones (someone might be working late), escalates to cell phones, and only goes to voicemail as a last resort.
Requirements
followme.confor a[followme-external]context with cell phone numbers- Custom greeting audio files (with automatic fallback to built-in prompts)
func_stat.somodule loaded (for STAT() file existence checks)
After-Hours Context
[afterhours-sales]
exten => s,1,NoOp(After Hours - Sales)
same => n,Answer()
same => n,Wait(1)
; Play custom greeting if the file exists, otherwise use built-in prompts
same => n,Set(AH_GREETING=/var/lib/asterisk/sounds/custom/afterhours-sales)
same => n,GotoIf($[${STAT(e,${AH_GREETING}.wav)} | ${STAT(e,${AH_GREETING}.ulaw)}]?play_custom)
; Fallback: generic after-hours message from built-in sounds
same => n,Playback(thank-you-for-calling)
same => n,Playback(hours)
same => n,Playback(vm-runout)
same => n,Goto(try_desk)
same => n(play_custom),Playback(${AH_GREETING})
; Tier 1: Ring desk phones
same => n(try_desk),NoOp(Ringing desk phones)
same => n,Dial(PJSIP/1001&PJSIP/1002&PJSIP/1003,20,tTr)
same => n,GotoIf($["${DIALSTATUS}" = "ANSWER"]?done)
; Tier 2: Escalate to cell phones via subroutine
same => n,NoOp(No answer at desk, trying cell phones)
same => n,Gosub(followme-all,s,1)
same => n,GotoIf($["${GOSUB_RETVAL}" = "ANSWERED"]?done)
; Tier 3: Voicemail
same => n,NoOp(No answer anywhere, going to voicemail)
same => n,VoiceMail(sales@default,u)
same => n(done),Hangup()
Cell Phone Subroutine
[followme-all]
exten => s,1,NoOp(Ringing all cell phones simultaneously)
; Use Local channels so each cell call routes through the external
; dialing context (which sets proper outbound caller ID)
same => n,Set(CELLS=Local/CELL_1001@followme-external&Local/CELL_1002@followme-external&Local/CELL_1003@followme-external)
same => n,Dial(${CELLS},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()
[followme-external]
; Each CELL_XXXX extension dials out through the trunk
exten => CELL_1001,1,NoOp(Follow Me to 1001 cell)
same => n,Set(CALLERID(num)=${IF($["${INCOMING_DID}" != ""]?${INCOMING_DID}:${MAIN_DID})})
same => n,Dial(PJSIP/18025551001@${TRUNK},30,tT)
same => n,Hangup()
exten => CELL_1002,1,NoOp(Follow Me to 1002 cell)
same => n,Set(CALLERID(num)=${IF($["${INCOMING_DID}" != ""]?${INCOMING_DID}:${MAIN_DID})})
same => n,Dial(PJSIP/18025551002@${TRUNK},30,tT)
same => n,Hangup()
How it works
- STAT(e, path): Checks if a file exists. Returns 1 if it exists, 0 otherwise. This lets you deploy custom greetings per department without changing the dialplan. just drop the file in place.
- Tiered fallback: Each tier attempts to reach someone. If
DIALSTATUSisANSWER, we skip straight todone. Otherwise we fall through to the next tier. - Local channels for cell phones:
Local/CELL_1001@followme-externalcreates a "virtual" channel that executes theCELL_1001extension in thefollowme-externalcontext. This lets you set outbound caller ID and route through the trunk properly for each cell call. - Caller ID preservation: The
followme-externalcontext sets the caller ID to the original inbound DID (stored earlier as__INCOMING_DID). The double underscore (__) makes the variable inherit across channels, including Local channel hops. - GOSUB_RETVAL: The subroutine communicates back to the caller via this special variable, which
Gosub()can read afterReturn().
Tips
- Set
__INCOMING_DID(double underscore) early in your inbound context so it propagates through Local channels. - The Wait(1) after Answer() prevents audio clipping: some carriers need a moment before audio is ready.
- Test the STAT() check by temporarily renaming the greeting file and confirming the fallback plays.
- For a more sophisticated setup, use
followme.confwith sequential number entries instead of the manual[followme-external]context.
User Notes
No notes yet. Be the first to contribute a tip or example.
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.