Skip to content
ViciDial AI ViciDial AI ViciDial AI

The VICIdial Community Blog

ViciDial AI ViciDial AI ViciDial AI

The VICIdial Community Blog

  • Home
  • Vicidial Hosting
  • Home
  • Vicidial Hosting
Close

Search

  • Home
  • Vicidial Hosting
Subscribe
Measurement ruler over a row of character cells, marking a substring offset and length.
AsteriskDialplan

Asterisk Variable Manipulation: Substrings, Math and Caller ID Rewriting

By okonkwo
October 12, 2023 3 Min Read
0

Most real dialplan work is string surgery. Strip a prefix, add a country code, extract a DID from a header, normalise a caller ID. Asterisk’s variable syntax handles all of it, but the substring notation with negative values confuses almost everyone the first time.

Here is the whole thing with worked examples.

Setting and Reading

exten => 100,1,Set(MYVAR=hello)
exten => 100,n,NoOp(${MYVAR})

NoOp() does nothing except print to the CLI, which makes it the workhorse of dialplan debugging. Run asterisk -rvvvvv and watch values as they change.

Channel variables are per-call. To set something globally, use the [globals] context or Set(GLOBAL(NAME)=value).

Inherit a variable across a transferred or bridged channel by prefixing with __:

exten => 100,1,Set(__CUSTOMERID=88421)

One underscore inherits one level. Two underscores inherit indefinitely. In a dialer where calls get transferred to closers, you almost always want two.

Substrings

The syntax:

${VARIABLE:offset:length}

Take EXTEN holding 12345678.

Positive Offset

Skip characters from the left.

${EXTEN:2}     →  345678
${EXTEN:4}     →  5678

This is what strips the VICIdial dial prefix. With EXTEN = 915551234567:

${EXTEN:1}     →  15551234567

Positive Offset With Length

${EXTEN:2:3}   →  345
${EXTEN:0:4}   →  1234

Skip 2, take 3. Skip 0, take 4.

Negative Offset

This is the part that trips people. A negative offset counts from the end and returns everything from that point forward.

${EXTEN:-4}    →  5678
${EXTEN:-2}    →  78

Useful for grabbing the last four digits of an account number, or matching a DID where the carrier’s prefix varies.

Negative Offset With Positive Length

Start from the end, then take that many characters going forward.

${EXTEN:-4:2}  →  56

Counts back 4 to position 5, then takes 2 characters: 56.

Negative Length

A negative length means “stop that many characters before the end.”

${EXTEN:0:-2}  →  123456
${EXTEN:2:-2}  →  3456

${EXTEN:0:-2} is the clean way to drop a trailing extension from a dialled string.

Concatenation

Just place variables and literals next to each other:

exten => _9X.,1,Set(OUTNUM=00${EXTEN:1})
exten => _9X.,n,NoOp(${OUTNUM})

Prepend a country code, strip a national trunk prefix:

; UK: 07911123456 → 447911123456
exten => _90X.,1,Set(OUTNUM=44${EXTEN:2})

Arithmetic

${MATH(expression,type)}

Type is int, float or hex.

exten => 100,1,Set(TOTAL=${MATH(5+3,int)})          ; 8
exten => 100,n,Set(SECS=${MATH(${MINS}*60,int)})
exten => 100,n,Set(PCT=${MATH(${ANSWERED}*100/${DIALED},float)})

Older dialplan used $[ ] expression syntax, which still works:

exten => 100,1,Set(TOTAL=$[5 + 3])

Whitespace inside $[ ] matters; $[5+3] can behave unexpectedly depending on version. MATH() is more predictable, prefer it.

Conditionals

${IF($[condition]?true_value:false_value)}
exten => _9X.,1,Set(PREFIX=${IF($[${LEN(${EXTEN:1})} = 10]?1:)})
exten => _9X.,n,Dial(SIP/mytrunk/${PREFIX}${EXTEN:1},,tTo)

That adds a 1 only when the number is 10 digits.

For branching rather than value selection, use GotoIf:

exten => _9X.,1,GotoIf($[${LEN(${EXTEN:1})} < 10]?invalid,1)
exten => _9X.,n,Dial(SIP/mytrunk/${EXTEN:1},,tTo)
exten => _9X.,n,Hangup()

exten => invalid,1,Playback(invalid)
exten => invalid,n,Hangup()

CUT — Splitting on a Delimiter

${CUT(varname,delimiter,field)}

Useful when a variable carries structured data:

exten => 100,1,Set(DATA=john|smith|5551234)
exten => 100,n,Set(FIRST=${CUT(DATA,|,1)})    ; john
exten => 100,n,Set(PHONE=${CUT(DATA,|,3)})    ; 5551234

Extracting a user from a SIP URI:

exten => _X.,1,Set(FULL=${CUT(SIP_HEADER(To),@,1)})
exten => _X.,n,Set(DID=${CUT(FULL,:,2)})

SIP_HEADER(To) returns something like <sip:15551234567@10.0.0.1>. First CUT on @ gives <sip:15551234567, second on : gives 15551234567.

For PJSIP, the equivalent is PJSIP_HEADER(read,To).

REGEX

Returns 1 or 0:

exten => _X.,1,GotoIf($[${REGEX("^1[2-9][0-9]{9}$" ${EXTEN})}]?valid,1:invalid,1)

Useful as a validity gate before handing a number to a carrier that charges for invalid attempts.

Caller ID Rewriting

Read and write with the CALLERID() function:

exten => _9X.,1,NoOp(Inbound CID: ${CALLERID(num)})
exten => _9X.,n,Set(CALLERID(num)=15551230000)
exten => _9X.,n,Set(CALLERID(name)=Acme Support)

Strip a leading + from an inbound caller ID:

exten => _X.,1,Set(CALLERID(num)=${IF($["${CALLERID(num):0:1}" = "+"]?${CALLERID(num):1}:${CALLERID(num)})})

Normalise a 10-digit inbound to 11-digit for CRM matching:

exten => _X.,1,Set(CIDLEN=${LEN(${CALLERID(num)})})
exten => _X.,n,ExecIf($[${CIDLEN} = 10]?Set(CALLERID(num)=1${CALLERID(num)}))

ExecIf() runs an application conditionally on one line, which keeps this readable.

Local Presence Dialling

A common dialer requirement — present a caller ID matching the area code you are calling:

exten => _91NXXNXXXXXX,1,AGI(agi://127.0.0.1:4577/call_log)
exten => _91NXXNXXXXXX,n,Set(NPA=${EXTEN:2:3})
exten => _91NXXNXXXXXX,n,Set(CALLERID(num)=${DB(cidmap/${NPA})})
exten => _91NXXNXXXXXX,n,ExecIf($["${CALLERID(num)}" = ""]?Set(CALLERID(num)=15551230000))
exten => _91NXXNXXXXXX,n,Dial(SIP/mytrunk/${EXTEN:1},,tTo)
exten => _91NXXNXXXXXX,n,Hangup()

Populate the AstDB from the CLI:

asterisk -rx "database put cidmap 212 12125551000"
asterisk -rx "database put cidmap 312 13125551000"
asterisk -rx "database show cidmap"

Be aware that caller ID practices are regulated in many jurisdictions, and STIR/SHAKEN attestation in the US depends on the number being one you are authorised to use. Check with your carrier before deploying anything like this.

Debugging

asterisk -rvvvvv
core set verbose 5
dialplan show default

Sprinkle NoOp() liberally while developing:

exten => _9X.,n,NoOp(EXTEN=${EXTEN} STRIPPED=${EXTEN:1} CID=${CALLERID(num)})

Then remove them once it works. Every NoOp on a high-volume dialer is a log write.

Quick Reference

ExpressionResult on 12345678
${EXTEN:1}2345678
${EXTEN:0:3}123
${EXTEN:-3}678
${EXTEN:-4:2}56
${EXTEN:0:-2}123456
${LEN(${EXTEN})}8

Tags:

asteriskcalleridcutdialplanmathregexsubstringvariablesvicidial
Author

okonkwo

Follow Me
Other Articles
Two parallel signalling rails converging at endpoints, representing chan_sip and PJSIP running side by side.
Previous

PJSIP Trunks in VICIdial: A Working Asterisk 18 Configuration

A call route that terminates at a marked failure point, continuing only as a broken dashed line.
Next

Fixing ‘Extension s Rejected Because Extension Not Found’ on Inbound Calls

No Comment! Be the first one.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

About This Site

Vicidial, Asterisk, GoAutoDial and FreePBX tutorials — with practical guides to AI answering machine detection and AI agent integration.

Search

Recent Posts

  • Piping VICIdial Call Recordings Into a Speech-to-Text Workflow
  • Fixing ‘Extension s Rejected Because Extension Not Found’ on Inbound Calls
  • Asterisk Variable Manipulation: Substrings, Math and Caller ID Rewriting
  • PJSIP Trunks in VICIdial: A Working Asterisk 18 Configuration
  • Installing VICIphone WebRTC with Let’s Encrypt SSL on ViciBox 11

ViciDial AI

Vicidial, Asterisk, GoAutoDial and FreePBX tutorials — with practical guides to AI answering machine detection and AI agent integration.

Recent Posts

  • Piping VICIdial Call Recordings Into a Speech-to-Text Workflow
  • Fixing ‘Extension s Rejected Because Extension Not Found’ on Inbound Calls
  • Asterisk Variable Manipulation: Substrings, Math and Caller ID Rewriting
  • PJSIP Trunks in VICIdial: A Working Asterisk 18 Configuration
  • Installing VICIphone WebRTC with Let’s Encrypt SSL on ViciBox 11

Archives

  • April 2024 (1)
  • January 2024 (1)
  • October 2023 (1)
  • August 2023 (1)
  • May 2023 (1)
  • February 2023 (1)
  • November 2022 (1)
  • September 2022 (1)
  • June 2022 (1)
  • March 2022 (1)

Find Us

Contact Us:

email: info@vicidialai.com

Copyright 2026 — ViciDial AI. All rights reserved. | Privacy Policy