VMware ESXi and vSphere Cluster Management
Asterisk Dialplan Variables
Learn how to set, expand, scope, debug, and safely use channel, global, shared, and environment variables in an Asterisk dialplan.
An Asterisk dialplan variable is a named value that the dialplan can store and use later. Variables let you avoid repeating literal endpoints, extensions, context names, caller attributes, and routing choices.
For example, instead of placing the same operator endpoint in several applications, store it once and reference it wherever needed. Variables also make conditional routing easier: a dialplan can choose a destination, save it, inspect it, and use it in Dial(), Goto(), or another application.
Setting and reading a variable
Set() is the standard dialplan application for assigning a value. Its basic format is Set(NAME=value).
[local]
exten => 432,1,Set(JOHN=SIP/001565123123)
same => n,Dial(${JOHN})
The first priority assigns the value SIP/001565123123 to JOHN. The next priority reads the value with ${JOHN}.
${VARIABLE_NAME} is variable expansion syntax. Before an application runs, Asterisk replaces the expression with the value currently associated with the variable. Thus, Set(JOHN=SIP/001565123123) defines a value, while Dial(${JOHN}) consumes that value.
In the example, the called extension is 432. Execution enters that extension, runs Set(), advances with same => n, expands ${JOHN}, and then supplies the resulting endpoint to Dial(). The effective call is equivalent to:
Dial(SIP/001565123123)
Variable syntax reference
| Task | Syntax | Meaning | Example |
|---|---|---|---|
| Assign a variable with Set() | Set(NAME=value) | Stores a value | Set(ROUTE=SIP/200) |
| Read a variable | ${NAME} | Expands to the current value | Dial(${ROUTE}) |
| Define a global | NAME=value in [globals] | Creates a broadly available dialplan value | DEFAULT_OPERATOR=SIP/200 |
| Access a shared variable | ${SHARED(key)} | Reads shared state by key | ${SHARED(route_abc123)} |
| Access an environment value | ${ENV(NAME)} | Reads an Asterisk process environment entry | ${ENV(DEPLOYMENT)} |
| Print a value | NoOp(text=${NAME}) | Writes an inspection message to the CLI | NoOp(Route=${ROUTE}) |
Case and naming behavior
Asterisk variable lookup is generally case-insensitive. In ordinary dialplan variable use, JOHN, John, and john should not be treated as three reliably separate variables; they refer to the same variable name under normal Asterisk behavior. Variable values, such as endpoint strings, can still contain case-sensitive data for the system consuming them.
[variable-test]
exten => 433,1,Set(JOHN=uppercase)
same => n,Set(John=mixedcase)
same => n,NoOp(JOHN=${JOHN}; John=${John}; john=${john})
same => n,Hangup()
The second assignment can overwrite the first because the names are normally equivalent. Use a consistent convention, such as uppercase names for custom dialplan variables, and avoid relying on capitalization to distinguish values.
Variable types and scope
Scope describes where a value can be seen and how long it is useful. A call-specific value should usually be a channel variable. A system-wide default belongs in a global variable. Shared and environment variables have broader effects and require more careful naming and cleanup.
| Type | How It Is Accessed | Visibility | Typical Lifetime | Appropriate Uses | Key Cautions |
|---|---|---|---|---|---|
| Channel | Set(NAME=value), ${NAME} | One call channel | Usually the channel lifetime | Per-call route, caller data, temporary decisions | May not automatically appear on a newly created channel |
| Global | [globals] or global assignment | Available across the dialplan | Until changed or Asterisk restarts, depending on how it was set | Defaults and stable shared configuration | Concurrent calls can see changes; do not use for temporary call state |
| Shared | SHARED(key) | Coordinated across channels by key | Until cleared or otherwise removed | Short-lived cross-channel correlation | Key collisions, stale data, and concurrency problems |
| Environment | ENV(name) | Asterisk process environment | Process or environment dependent | Carefully controlled process-level settings | Not call-scoped; may expose or alter process configuration |
Channel variables
A channel variable belongs to an individual call channel. It is the normal choice for information such as a selected queue, a caller classification, or a destination calculated during the current call.
[local]
exten => 440,1,Set(ROUTE=support)
same => n,NoOp(Selected route is ${ROUTE})
same => n,Goto(routes,${ROUTE},1)
When an application creates another channel, such as during dialing, channel-variable visibility depends on the call flow and the deployed Asterisk version. A channel variable may need an underscore-prefixed inheritance form where supported: a single underscore can make a value available to a child channel, while a double underscore can continue inheritance through further channel creation. Verify the exact inheritance behavior for your Asterisk version and application path. Do not use inheritance as a substitute for deliberate cross-channel coordination.
Global variables
Global variables are available across the dialplan rather than being restricted to one channel. They are useful for reusable defaults.
[globals]
DEFAULT_OPERATOR=SIP/001565123123
[local]
exten => 434,1,Dial(${DEFAULT_OPERATOR})
A global is appropriate for a default operator or a stable routing policy. It is not appropriate for a caller's temporary destination or authentication state. If one call changes a global while another call is using it, the second call may observe the new value. Changing globals while the PBX is running therefore has operational consequences: test the change, understand which calls can see it, and reload or update configuration using your normal change procedure.
Shared variables
Shared variables use the SHARED() function and a key-based access model. They are useful when related channels need short-lived coordination data. A key should be unique to the call or transaction, such as a linked call identifier.
[local]
exten => 435,1,Set(CORRELATION_KEY=${CHANNEL(linkedid)})
same => n,Set(SHARED(route_${CORRELATION_KEY})=support)
same => n,NoOp(Shared route is ${SHARED(route_${CORRELATION_KEY})})
same => n,Set(SHARED(route_${CORRELATION_KEY})=)
same => n,Hangup()
This example uses the linked identifier as part of the shared-variable key; it does not pass that identifier as a channel-name argument. If you use the optional second argument to SHARED(), it identifies a channel and must be an actual channel name, not merely a linked call identifier. Related channels must know the same collision-resistant key to retrieve the value. Clear temporary shared state after use.
Environment variables
Environment variables are entries in the environment of the Asterisk process. Where supported by the deployed version, the ENV() function can retrieve or set an environment entry, for example ${ENV(DEPLOYMENT)}.
[local]
exten => 450,1,NoOp(PBX deployment is ${ENV(DEPLOYMENT)})
same => n,Hangup()
Environment scope is different from channel scope. It is not a safe place for temporary per-call data, and values may contain secrets that should not be printed. Use process-environment access only for carefully controlled integration or configuration needs, and confirm read/set behavior for your Asterisk version.
Variables in call-routing logic
A variable can hold an endpoint, extension, context, caller-related attribute, or validated routing choice. It can be expanded inside application arguments.
[routing]
exten => 460,1,Set(DESTINATION=SIP/200)
same => n,Dial(${DESTINATION},20)
exten => 461,1,Set(NEXT_CONTEXT=queues)
same => n,Set(NEXT_EXTENSION=support)
same => n,Goto(${NEXT_CONTEXT},${NEXT_EXTENSION},1)
Dynamic construction is useful, but values that influence Dial() or Goto() must be validated. Prefer mapping untrusted input to a small set of known destinations instead of allowing a caller-provided string to become an arbitrary endpoint, context, extension, or priority.
Validated routing pattern
[incoming]
exten => 470,1,Set(CHOICE=support)
same => n,GotoIf($["${CHOICE}" = "support"]?routes,support,1)
same => n,GotoIf($["${CHOICE}" = "sales"]?routes,sales,1)
same => n,Hangup()
[routes]
exten => support,1,Dial(SIP/200,20)
same => n,Hangup()
exten => sales,1,Dial(SIP/201,20)
same => n,Hangup()
Observing variable expansion
Start the Asterisk CLI with increased verbosity while testing:
asterisk -rvvv
For the basic example, the configured expression contains ${JOHN}, while the runtime execution line shows the expanded endpoint. Representative output may look like this:
-- Executing [432@local:1] Set("PJSIP/100-00000001", "JOHN=SIP/001565123123") in new stack
-- Executing [432@local:2] Dial("PJSIP/100-00000001", "SIP/001565123123") in new stack
The first line shows the assignment argument. The second line shows the final argument passed to Dial(). In the dialplan source, that second argument was written as ${JOHN}; Asterisk expanded it before executing the application.
Use NoOp() to inspect a value at a precise point:
same => n,NoOp(DEBUG route=${ROUTE} extension=${EXTEN} linkedid=${CHANNEL(linkedid)})
Use dialplan reload after configuration changes and dialplan show local to inspect the loaded context:
dialplan reload
dialplan show local
Common problems and safe practices
| Symptom | Likely Cause | How to Verify | Resolution |
|---|---|---|---|
| Empty expansion | Variable was not assigned on this path, was misspelled, or exists only on another channel | Add NoOp(VALUE=${VARIABLE_NAME}); inspect preceding CLI lines and capitalization | Assign before reading; use the correct scope or inheritance mechanism |
| Unexpected literal text | ${...} was omitted | Compare the source argument with the CLI execution line | Use ${VARIABLE_NAME} when the value, not the name, is required |
| Wrong value due to capitalization | Conflicting assignments or an assumption that case distinguishes variables | Inspect every assignment and reference | Use descriptive, consistently uppercase custom names |
| Value unavailable on another channel | Channel variables are local and were not inherited | Identify the channel where Set() ran and trace channel creation | Use suitable inheritance or SHARED() with a unique key |
| Shared-state collision | Concurrent calls reused a generic key or stale state was not cleared | Log the key and test concurrent calls | Include a unique call or transaction identifier and clear temporary state |
| Unsafe dynamic routing | Untrusted input directly controls Dial() or Goto() | Log the resolved destination and review its source | Validate against known endpoints, contexts, and extensions |
Practical checklist
- Assign with
Set(NAME=value)before reading the value. - Use
${NAME}inside application arguments that need expansion. - Use channel variables for ordinary per-call state.
- Use globals for stable defaults, not temporary call data.
- Use
SHARED()only when cross-channel coordination is needed, with unique keys and cleanup. - Treat environment values as process-level configuration, not call state.
- Use consistent naming; do not depend on letter case to create separate variables.
- Inspect expansion with CLI verbosity and carefully selected
NoOp()messages. - Validate every variable-derived
Dial()orGoto()destination.
Variables are most effective when their scope matches their purpose: local channel data stays local, reusable defaults are global, coordinated temporary state is shared deliberately, and process configuration remains in the environment. That separation makes routing easier to read and reduces accidental cross-call interference.