When turning up any type of new voice service, there are requirements for calling and called numbers that we, as voice engineers, must ensure are met.
Note: I’m not going to go in-depth into raw SIP packet captures in this article—I’m going to focus on the theory and IOS XE configurations. If you want to see the SIP headers change in real time, capture a call in a lab CUBE, follow this article to create translation rules, and inspect the resulting SIP INVITE messages.
For example, most SIP service providers (ITSPs) will reject an outbound call if the Calling Party Number does not match a valid 10-digit North American Numbering Plan (NANP) format. If an internal extension (1000) places an outbound call without a 10-digit mask, the ITSP sees From: 1000, doesn’t recognize 1000 as a valid DID, and denies the call.
How do we fix this? Since we control our CUCM, the primary place to format Caller ID is directly on the Directory Number configuration page using the External Phone Number Mask field (for example, 303618XXXX, which turns extension 1000 into 303-618-1000).
Configuration Note: Sending a call out without an External Phone Number Mask sends only the internal DN (
1000). Adding an External Phone Number Mask (303618XXXX) usesXwildcards to expand1000into303-618-1000(assuming the outbound Route Pattern / Route List has Use Calling Party’s External Phone Number Mask checked).
Now that we have that configured in CUCM, let’s add an extra layer of protection in our Cisco CUBE—just in case a technician or engineer forgets to add an External Phone Number Mask on a newly provisioned line—so outbound calls still format properly and complete.
Voice Translations in Cisco IOS XE
Before we dig into the CLI commands, let’s review the 4-step hierarchy of IOS XE voice translations:
- A
voice translation-ruleis a numbered set of regex rules:- Rules can be written to manipulate, allow, or reject calls.
- The
voice translation-ruleis referenced inside avoice translation-profile:- Inside the profile, you specify whether the rule applies to the
calling,called, orredirect-callednumber.
- Inside the profile, you specify whether the rule applies to the
- The
voice translation-profileis assigned to adial-peer:- When applied to a dial peer, you specify the call leg direction (
incomingoroutgoing).
- When applied to a dial peer, you specify the call leg direction (
- Test the rules using
test voice translation-rule:- Built-in IOS CLI testing lets you verify regex matches immediately without placing live test calls.
[voice translation-rule 1]
└── rule 11 /^\(....\)$/ /303618\1/
│
▼ (translate calling 1)
[voice translation-profile DN-to-NANP]
│
▼ (translation-profile outgoing DN-to-NANP)
[dial-peer voice 100 voip]
Step 1: Build the voice translation-rule
Generally speaking, let the CUBE handle protocol interworking and digit normalization, and leave call-blocking lists to CUCM. Here we will focus purely on digit manipulation.
Suppose our call is going to 303-618-1234 coming from internal DN 1000. Using regular expressions (as discussed in Cisco Expressways and my Toll Fraud battle), we could match 1000 literally:
voice translation-rule 1
rule 11 /1000/ /3036181000/
In the snippet above, voice translation-rule 1 defines ruleset 1, and rule 11 is the first rule in ruleset 1 (followed by 12, 13, etc.). Forward slashes (/original/ /replacement/) delimit the match pattern and replacement pattern.
However, we have more than just one extension making calls over this SIP trunk. Let’s make the rule generic so it catches any 4-digit extension that arrives at the CUBE without a 10-digit DID mask:
voice translation-rule 1
rule 11 /^\(....\)$/ /303618\1/
Let’s break down /^\(....\)$/ /303618\1/:
^matches the start of the number string.\(and\)define Capture Group 1 (in IOS SED-style regex, parentheses are escaped with backslashes).....matches any four single characters/digits.$matches the end of the number string./^\(....\)$/means: “Match only if the entire number is exactly four digits long.”/303618\1/means: “Prepend303618followed by Capture Group 1 (\1).”
So if a call enters the rule with a calling number of 1000, it exits as 3036181000. If it enters as 1150, it exits as 3036181150. If a call already has a 10-digit calling number (3036181000), it does not match ^\(....\)$ and passes through untouched.
Step 2: Apply the Rule to a voice translation-profile
Now we bind voice translation-rule 1 to the calling number inside a named voice translation-profile:
voice translation-profile DN-to-NANP
translate calling 1
Step 3: Assign the voice translation-profile to the Dial Peer
Next, we apply DN-to-NANP in the outgoing direction on our outbound ITSP VoIP dial peer:
dial-peer voice 100 voip
description *** Outbound To ITSP ***
session protocol sipv2
translation-profile outgoing DN-to-NANP
destination-pattern T
session target ipv4:172.16.1.1
Step 4: Test Your Rule in CLI
We can verify our translation rule directly in privileged EXEC mode using test voice translation-rule [rule-number] [input-digits]:
Router1#test voice translation-rule 1 1234
Matched with rule 11
Original number: 1234 Translated number: 3036181234
As shown above, 1234 matched our rule and translated cleanly to 3036181234. Now, even if an External Phone Number Mask is accidentally omitted in CUCM, the CUBE guarantees a valid 10-digit NANP calling number on the outbound SIP trunk.
References
- Voice Translation Rules: https://www.cisco.com/c/en/us/support/docs/voice/call-routing-dial-plans/61083-voice-transla-rules.html
- Number Translation using Voice Translation Profiles: https://www.cisco.com/c/en/us/support/docs/voice/call-routing-dial-plans/64020-number-voice-translation-profiles.html

Jaron Davis
Unified Communications Architect specializing in enterprise Zoom, Microsoft Teams, Cisco UC modernization, SIP/VoIP infrastructure, and systems architecture.