Macro to Gosub Migration
Macro to Gosub Migration
Old Asterisk dialplans often use Macro() for reusable call logic. New dialplans should use Gosub() instead. Gosub() is clearer, nests correctly, returns explicitly, and is the supported pattern after app_macro was removed in Asterisk 21. Asterisk 20 and older releases may still load it, but it is deprecated and should be migrated.
Macro pattern
A classic macro might look like this:
[macro-dial-user]
exten => s,1,NoOp(Dialing ${ARG1})
same => n,Dial(PJSIP/${ARG1},20)
same => n,MacroExit()
[internal]
exten => _1XX,1,Macro(dial-user,${EXTEN})
same => n,Hangup()
Inside a macro, arguments are exposed as ${ARG1}, ${ARG2}, and so on. The macro always starts at s,1 in a 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. named macro-name.
Gosub equivalent
[sub-dial-user]
exten => s,1,NoOp(Dialing ${ARG1})
same => n,Dial(PJSIP/${ARG1},20)
same => n,Return()
[internal]
exten => _1XX,1,Gosub(sub-dial-user,s,1(${EXTEN}))
same => n,Hangup()
The arguments still appear as ${ARG1}, ${ARG2}, and so on. The difference is that the destination is explicit: context, extensionA dialplan entry that matches a dialed number or pattern within a context and triggers a sequence of prioritized steps., priority, and optional argument list.
Migration rules
| Macro | Gosub |
|---|---|
Macro(name,arg1,arg2) |
Gosub(sub-name,s,1(arg1,arg2)) |
[macro-name] |
[sub-name] or another normal context name |
MacroExit() |
Return() |
${MACRO_EXTEN} |
Pass ${EXTEN} as an argument when needed |
${MACRO_CONTEXT} |
Pass ${CONTEXT} as an argument when needed |
${MACRO_PRIORITY} |
Pass priority only if the subroutine really needs it |
Before and after example
; Before
[macro-record-and-dial]
exten => s,1,MixMonitor(${UNIQUEID}.wav)
same => n,Dial(PJSIP/${ARG1},${ARG2})
same => n,MacroExit()
[internal]
exten => _1XX,1,Macro(record-and-dial,${EXTEN},25)
same => n,Hangup()
; After
[sub-record-and-dial]
exten => s,1,MixMonitor(${UNIQUEID}.wav)
same => n,Dial(PJSIP/${ARG1},${ARG2})
same => n,Return()
[internal]
exten => _1XX,1,Gosub(sub-record-and-dial,s,1(${EXTEN},25))
same => n,Hangup()
Common mistakes
- Forgetting
Return(), which causes 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 to continue into whatever follows the subroutine. - Using
MacroExit()in a Gosub context. - Relying on
${MACRO_EXTEN}without passing${EXTEN}into the subroutine. - Naming subroutine contexts
macro-..., which confuses future readers even if it still works as a context name.
See Also
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.