Skip to main content

Dialplan Configuration & Call Routing

๐Ÿ“– Back to Main Documentation

Comprehensive guide to XML dialplan configuration, call routing logic, and dialplan variables.

Core Documentationโ€‹

Call Processing Flowโ€‹

  • ๐Ÿ”ข Number Translation - E.164 normalization (happens before dialplan)
  • ๐Ÿ‘ฅ Sh Interface - Subscriber data retrieved for dialplan variables
  • ๐Ÿ“ก SS7 MAP - MSRN/HLR data in dialplan variables
  • ๐Ÿ’ณ Online Charging - OCS authorization in call flow

Services Implementationโ€‹

  • โš™๏ธ Supplementary Services - Implementing call forwarding, CLI blocking in dialplan
  • ๐Ÿ“ž Voicemail - Voicemail routing and deposit/retrieval in dialplan
  • ๐Ÿ”Š TTS Prompts - Using prompts in dialplan with playback

Monitoringโ€‹


Dialplan Config / Call Routingโ€‹

The TAS uses XML dialplans with a schema compatible with standard telecom XML dialplan formats, with variables populated by the TAS. This means you can define your own dialplan as needed, with the business logic for the operator, but have all the required data such as Repository Data, SS7 routing info, IMPI / IMPU identities, dialplan normalization, etc, etc.

Dialplans are written into priv/templates and take the form:

  • mo_dialplan.xml - Mobile Originated Call Dialplan
  • mo_emergency_dialplan.xml - Mobile Originated Emergency Call Dialplan
  • mt_dialplan.xml - Mobile Terminated Call Dialplan

You can view the Dialplans from inside the Web UI.

Various variables are set by the TAS before the XML gets parsed, these variables are printed to the log at the start of the call with their current values and are very helpful when defining your own call logic.

FreeSWITCH XML Dialplan Fundamentalsโ€‹

OmniTAS uses the same XML call routing system as the FreeSWITCH project, which allows for flexible call routing to meet your needs.

This section explains the core concepts and provides practical examples.

Basic Structureโ€‹

A dialplan consists of extensions containing conditions and actions:

<extension name="description-of-what-this-does">
<condition field="${variable}" expression="regex-pattern">
<action application="app_name" data="parameters"/>
<anti-action application="app_name" data="parameters"/>
</condition>
</extension>

Extensions are evaluated in order from top to bottom. When a condition matches, its actions execute.

Conditions and Regex Matchingโ€‹

Conditions test variables against regular expressions. If the regex matches, actions execute; if not, anti-actions execute.

Basic exact match:

<condition field="${tas_destination_number}" expression="2222">
<action application="log" data="INFO Calling voicemail access number"/>
</condition>

Multiple number match:

<condition field="${tas_destination_number}" expression="^(2222|3444|3445)$">
<action application="log" data="INFO Calling special service"/>
</condition>

Pattern matching with capture groups:

<condition field="${tas_destination_number}" expression="^1(8[0-9]{9})$">
<!-- Matches 1 followed by 8 and 9 more digits -->
<action application="log" data="INFO Matched toll-free: $1"/>
<action application="bridge" data="sofia/gateway/trunk/${tas_destination_number}"/>
</condition>

Prefix matching:

<condition field="${tas_destination_number}" expression="^00">
<!-- Matches any number starting with 00 (international) -->
<action application="log" data="INFO International call detected"/>
</condition>

Range matching:

<condition field="${msisdn}" expression="^5551241[0-9]{4}$">
<!-- Matches 55512410000 through 55512419999 -->
<action application="log" data="INFO Subscriber in range"/>
</condition>

Actions vs Anti-Actionsโ€‹

Actions execute when a condition matches. Anti-actions execute when a condition does NOT match.

<condition field="${cli_withheld}" expression="true">
<!-- Executes if CLI is withheld -->
<action application="set" data="effective_caller_id_number=anonymous"/>
<action application="set" data="origination_privacy=hide_number"/>

<!-- Executes if CLI is NOT withheld -->
<anti-action application="log" data="DEBUG CLI is normal"/>
<anti-action application="set" data="effective_caller_id_number=${msisdn}"/>
</condition>

The continue="true" Attributeโ€‹

By default, when an extension's condition matches, the dialplan stops processing further extensions. The continue="true" attribute allows processing to continue to the next extension.

Without continue (default behavior):

<extension name="First-Check">
<condition field="${tas_destination_number}" expression="^(.*)$">
<action application="log" data="INFO Processing call"/>
</condition>
</extension>

<extension name="Never-Reached">
<!-- This NEVER executes because the previous extension matched -->
<condition field="${tas_destination_number}" expression="^(.*)$">
<action application="log" data="INFO This won't print"/>
</condition>
</extension>

With continue="true":

<extension name="Print-Vars" continue="true">
<condition field="${tas_destination_number}" expression="^(.*)$">
<action application="info" data=""/>
</condition>
</extension>

<extension name="Check-Balance" continue="true">
<condition field="${hangup_case}" expression="OUTGOING_CALL_BARRED">
<action application="log" data="ERROR Insufficient balance"/>
<action application="hangup" data="${hangup_case}"/>
</condition>
</extension>

<extension name="Route-Call">
<!-- This extension still gets evaluated -->
<condition field="${tas_destination_number}" expression="^(.*)$">
<action application="bridge" data="sofia/gateway/trunk/${tas_destination_number}"/>
</condition>
</extension>

Use continue="true" for:

  • Logging/debugging extensions
  • Setting variables that apply to multiple scenarios
  • Validation checks that don't route the call

Common Applicationsโ€‹

call controlโ€‹

answer - Answer the call (send 200 OK)

<action application="answer" data=""/>

hangup - Terminate the call with a specific cause

<action application="hangup" data="NORMAL_CLEARING"/>
<action application="hangup" data="USER_BUSY"/>
<action application="hangup" data="NO_ANSWER"/>

bridge - Connect the call to another destination

<!-- Bridge to external gateway -->
<action application="bridge" data="sofia/gateway/trunk/+12125551234"/>

<!-- Bridge to internal extension with codec preferences -->
<action application="bridge" data="{absolute_codec_string=AMR-WB,AMR,PCMA}sofia/internal/sip:user@domain.com"/>

<!-- Bridge with timeout -->
<action application="bridge" data="{originate_timeout=30}sofia/gateway/trunk/${tas_destination_number}"/>
Variables and Channel Dataโ€‹

set - Set a channel variable

<action application="set" data="my_variable=my_value"/>
<action application="set" data="sip_h_X-Custom-Header=CustomValue"/>
<action application="set" data="effective_caller_id_number=anonymous"/>

unset - Remove a channel variable

<action application="unset" data="sip_h_P-Asserted-Identity"/>

export - Set variable and export to B-leg (bridged call)

<action application="export" data="sip_h_X-Account-Code=ABC123"/>
Media and Promptsโ€‹

playback - Play an audio file

<action application="playback" data="/sounds/en/us/callie/misc/8000/out_of_credit.wav"/>
<action application="playback" data="$${base_dir}/sounds/custom_prompt.wav"/>

sleep - Pause for specified milliseconds

<action application="sleep" data="1000"/>  <!-- Sleep for 1 second -->

echo - Echo audio back to caller (testing)

<action application="echo" data=""/>

conference - Place call into conference

<action application="conference" data="room-${destination_number}@wideband"/>
voicemailโ€‹

voicemail - Access voicemail system

<!-- Leave voicemail for mailbox -->
<action application="voicemail" data="default default ${msisdn}"/>

<!-- Check voicemail with auth -->
<action application="voicemail" data="check auth default default ${msisdn}"/>
Logging and Debuggingโ€‹

log - Write to log file

<action application="log" data="INFO Processing call from ${msisdn}"/>
<action application="log" data="DEBUG Destination: ${tas_destination_number}"/>
<action application="log" data="ERROR Call failed with cause: ${hangup_cause}"/>

info - Dump all channel variables to log

<action application="info" data=""/>
Misc Applicationsโ€‹

say - Text-to-speech number reading

<action application="say" data="en number iterated ${tas_destination_number}"/>

send_dtmf - Send DTMF tones

<action application="send_dtmf" data="1234#"/>

Practical Examplesโ€‹

Emergency Services Routing:

<extension name="Emergency-911">
<condition field="${tas_destination_number}" expression="^(911|112)$">
<action application="log" data="ALERT Emergency call from ${msisdn}"/>
<action application="answer" data=""/>
<action application="playback" data="/sounds/emergency_services_transfer.wav"/>
<action application="bridge" data="sofia/gateway/emergency_gw/${tas_destination_number}"/>
</condition>
</extension>

Conditional Routing Based on Balance:

<extension name="Check-Credit">
<condition field="${hangup_case}" expression="OUTGOING_CALL_BARRED">
<action application="answer" data=""/>
<action application="playback" data="/sounds/out_of_credit.wav"/>
<action application="hangup" data="CALL_REJECTED"/>
</condition>
</extension>

On-Net vs Off-Net Routing:

<extension name="Route-Decision">
<condition field="${on_net_status}" expression="true">
<!-- On-net: route back through TAS -->
<action application="log" data="INFO Routing to on-net subscriber"/>
<action application="bridge" data="sofia/internal/+${tas_destination_number}@10.179.3.60"/>
<anti-action application="log" data="INFO Routing off-net"/>
<anti-action application="bridge" data="sofia/gateway/trunk/+${tas_destination_number}"/>
</condition>
</extension>

Anonymous Caller ID Handling:

<extension name="CLI-Privacy" continue="true">
<condition field="${cli_withheld}" expression="true">
<action application="set" data="effective_caller_id_name=anonymous"/>
<action application="set" data="effective_caller_id_number=anonymous"/>
<action application="set" data="origination_privacy=hide_number"/>
</condition>
</extension>

Voicemail on No Answer:

<extension name="Try-Bridge-Then-VM">
<condition field="${tas_destination_number}" expression="^(555124115\d{2})$">
<action application="set" data="call_timeout=30"/>
<action application="bridge" data="sofia/internal/${tas_destination_number}@domain.com"/>

<!-- If bridge fails, go to voicemail -->
<action application="log" data="INFO Bridge failed, routing to voicemail"/>
<action application="answer" data=""/>
<action application="voicemail" data="default default ${tas_destination_number}"/>
</condition>
</extension>

Number Range Routing:

<extension name="Local-Numbers">
<condition field="${tas_destination_number}" expression="^([2-9]\d{2})$">
<!-- 3-digit local extensions 200-999 -->
<action application="log" data="INFO Local extension: $1"/>
<action application="bridge" data="sofia/internal/$1@pbx.local"/>
</condition>
</extension>

<extension name="National-Numbers">
<condition field="${tas_destination_number}" expression="^555\d{7}$">
<!-- National mobile numbers -->
<action application="log" data="INFO National mobile call"/>
<action application="bridge" data="sofia/gateway/national_trunk/${tas_destination_number}"/>
</condition>
</extension>

<extension name="International">
<condition field="${tas_destination_number}" expression="^00\d+$">
<!-- International calls starting with 00 -->
<action application="log" data="INFO International call"/>
<action application="bridge" data="sofia/gateway/intl_trunk/${tas_destination_number}"/>
</condition>
</extension>

Further Documentationโ€‹

For complete details on each application:

The FreeSWITCH wiki contains detailed documentation for every dialplan application, including all parameters and use cases.

Dialplan Variablesโ€‹

Variables set by the TAS in the XML dialplan logic:

Common Variables (All Call Types)โ€‹

Initial Setup:

  • destination_number - translated destination number
  • tas_destination_number - translated destination number
  • effective_caller_id_number - translated source number

Emergency Callsโ€‹

  • hangup_case - "none"
  • ims_private_identity - private user identity
  • ims_public_identity - public user identity
  • msisdn - subscriber number (stripped of +)
  • imsi - IMSI from private identity
  • ims_domain - domain from private identity

MT Calls (Mobile Terminated)โ€‹

  • ims_private_identity - private user identity
  • ims_public_identity - public user identity
  • msisdn - subscriber number (stripped of +)
  • imsi - IMSI from private identity
  • ims_domain - domain from private identity
  • call_forward_all_destination - CFA destination or "none"
  • call_forward_not_reachable_destination - CFNRc destination
  • scscf_address - S-CSCF address or "none"
  • scscf_domain - S-CSCF domain or "none"
  • no_reply_timer - timeout for no reply
  • hangup_case - "none" or "UNALLOCATED_NUMBER"
  • msrn - MSRN from PRN (if roaming) or forwarded number from SRI (if call forwarding active)
  • tas_destination_number - Routing destination override (set to MSRN or forwarded number)

MO Calls (Mobile Originated)โ€‹

  • hangup_case - "none", "OUTGOING_CALL_BARRED", or "UNALLOCATED_NUMBER"
  • ims_private_identity - private user identity
  • ims_public_identity - public user identity
  • msisdn - subscriber number (stripped of +)
  • imsi - IMSI from private identity
  • ims_domain - domain from private identity
  • allocated_time - time allocated by OCS (if online charging enabled)
  • cli_withheld - "true" or "false" string
  • on_net_status - "true" or "false" string (whether destination is on-net)
  • msrn - MSRN for roaming subscribers (if applicable)
  • tas_destination_number - MSRN override (if roaming)

Emergency Callingโ€‹

Emergency calling is controlled through the emergency_call_codes configuration parameter and is automatically detected during call authorization.

Configurationโ€‹

Configure the emergency call codes in your config/runtime.exs file:

config :tas,
emergency_call_codes: ["911", "912", "913", "sos"],
# ... other config

Configuration Details:

  • emergency_call_codes (required): List of strings representing emergency service numbers
  • These codes are checked in addition to SIP emergency URNs (e.g., <urn:service:sos>)
  • The system performs exact match comparison against the destination number
  • Common codes include: "911" (US), "112" (EU), "000" (AU), "999" (UK), "sos"

How Emergency Detection Worksโ€‹

The Tas.Dialplan.Authorization.is_emergency_call?/2 function checks two conditions:

  1. SIP URI Emergency Service URN: Detects <urn:service:sos> or any URI containing "service:sos"
  2. Destination Number Match: Compares Caller-Destination-Number against configured emergency_call_codes

If either condition is true, the call is classified as emergency.

Code Reference: See lib/dialplan/authorization.ex

Processing Flowโ€‹

Call Flow Details:

  1. Call arrives at TAS
  2. Authorization module checks destination against emergency patterns
  3. If emergency detected:
    • Call type is set to :emergency
    • mo_emergency_dialplan.xml template is used
    • OCS authorization is typically bypassed
    • Call is routed to PSAP gateway
  4. Metrics are recorded with call_type: emergency label

Dialplan Routingโ€‹

Define the routing for emergency calls in priv/templates/mo_emergency_dialplan.xml. This template determines how calls are routed to your PSAP (Public Safety Answering Point) gateway or SIP URI based on your market requirements.

Example emergency dialplan:

<extension name="Emergency-SOS">
<condition field="${destination_number}" expression="^(911|912|913|sos)$">
<action application="log" data="ALERT Emergency call from ${msisdn}"/>
<action application="answer" data=""/>
<action application="bridge" data="sofia/gateway/psap_gw/${destination_number}"/>
</condition>
</extension>

Best Practicesโ€‹

  • Always include "sos" in your emergency codes list for SIP URN compatibility
  • Include all local emergency numbers for your jurisdiction (e.g., 911, 112, 000, 999)
  • Test emergency routing regularly using the Call Simulator
  • Bypass OCS for emergency calls to ensure they always connect (configured via skipped_regex)
  • Configure PSAP gateway with high availability and redundancy
  • Monitor emergency call metrics to ensure system reliability

On-Net Mobile Originated call to an On-Net Mobile-Terminating Subscriberโ€‹

You can route from your dialplan to your dialplan through something like this:

<action application="bridge" data="{absolute_codec_string=AMR-WB,AMR,PCMA,PCMU,originate_retries=1,originate_timeout=60,sip_invite_call_id=${sip_call_id}}sofia/internal/+${tas_destination_number}@10.179.3.60" />

Where 10.179.3.60 is the TAS IP (It's routing the call back to the TAS) - Just make sure the TAS IP it's in your allowed_sbc_source_ips list.