Formatting Phone Numbers in Genesys Cloud Architect: From E.164 to National Format
What You Will Build
- A working Architect flow expression that converts an E.164 formatted phone number (e.g.,
+14155552671) into the standard US national format(415) 555-2671. - This solution uses the Genesys Cloud Architect Expression language (
expr) to manipulate strings and regular expressions without external API calls. - The logic is implemented directly within the Architect interface using a Script Action or a Set Variable action, requiring no external SDK or runtime dependencies.
Prerequisites
- Genesys Cloud Platform Access: You must have a user account with permissions to edit Architect flows (typically
architect:flow:editorarchitect:flow:read). - Architect Knowledge: Familiarity with creating Scripts, Flows, or IVRs in Genesys Cloud Architect.
- Data Source: A valid phone number variable in E.164 format (e.g.,
system.attributes.phone_numberor a custom variable like$.input_phone). Note that Genesys Cloud typically stores phone numbers in E.164 format by default. - No External Dependencies: This solution runs entirely within the Genesys Cloud edge network using the built-in expression engine. No Python, JavaScript, or external HTTP calls are required.
Authentication Setup
This tutorial does not require OAuth authentication for the end-user experience. The logic executes server-side within the Genesys Cloud Architect engine. However, if you are using the Genesys Cloud REST API to deploy this flow via CI/CD, you will need:
- OAuth Client Type: Client Credentials or JWT.
- Required Scopes:
architect:flow:editto save the flow. - SDK:
PureCloudPlatformClientV2(Python/Java/C#) or@genesyscloud/node-sdk(Node.js).
For this tutorial, we will focus on the Expression Logic itself, which you will paste into the Architect UI.
Implementation
Step 1: Understanding the Input Data
Genesys Cloud stores phone numbers in E.164 format. This format includes the country code prefix. For US and Canadian numbers, this is +1 followed by 10 digits.
Example Input: +14155552671
Our goal is to extract the Area Code (first 3 digits after the country code), the Prefix (next 3 digits), and the Line Number (last 4 digits) and format them as (XXX) XXX-XXXX.
If the input might come from different countries, we must handle the variable length of the country code. However, for a specific US/Canada use case, the logic is deterministic. For a global solution, we would need a more complex regex or a lookup table. This tutorial covers the US/Canada specific transformation, which is the most common requirement.
Step 2: Constructing the Regular Expression
The Genesys Cloud expression engine supports standard regular expressions via the ~ operator for matching and capture groups.
To parse +14155552671:
- We need to ignore the
+1prefix. - We need to capture the next 3 digits (Area Code).
- We need to capture the next 3 digits (Prefix).
- We need to capture the last 4 digits (Line Number).
The regex pattern is:
^\+1(\d{3})(\d{3})(\d{4})$
^: Start of string.\+1: Matches the literal+1.(\d{3}): Capture Group 1: 3 digits.(\d{3}): Capture Group 2: 3 digits.(\d{4}): Capture Group 3: 4 digits.$: End of string.
Step 3: Implementing the Logic in Architect
In Genesys Cloud Architect, you cannot use a simple replace function with backreferences like (\1) \2-\3. You must use the expr function with a conditional check or a more robust string manipulation approach.
The most reliable method in Architect is to use the regex function (available in newer versions) or a combination of left, right, and mid functions if the length is fixed. However, the regex function with capture groups is the cleanest.
Note: As of recent updates, Genesys Cloud Architect supports the regex function which returns a map of captured groups.
Option A: Using the regex Function (Recommended)
- Open your Flow or Script.
- Add a Script Action or a Set Variable action.
- Define the Output Variable: Create a variable named
formatted_phone. - Enter the Expression:
// This expression checks if the phone number matches the US/Canada E.164 pattern.
// If it matches, it formats it. If not, it returns the original number or an empty string.
if (
regex("+1(\\d{3})(\\d{3})(\\d{4})", $.input_phone) != null,
"(" + regex("+1(\\d{3})(\\d{3})(\\d{4})", $.input_phone).group1 + ") " +
regex("+1(\\d{3})(\\d{3})(\\d{4})", $.input_phone).group2 + "-" +
regex("+1(\\d{3})(\\d{3})(\\d{4})", $.input_phone).group3,
$.input_phone
)
Breakdown of the Expression:
regex(pattern, string): Attempts to match the pattern against the string. Returns a map with keysgroup0(full match),group1,group2, etc., ornullif no match.\\d{3}: In the Architect expression editor, backslashes must be escaped. So\dbecomes\\d.$.input_phone: The variable containing the raw E.164 number.group1,group2,group3: Accessing the captured groups from the regex result.- The
ifstatement ensures we do not crash if the input is not a valid US/Canada number.
Option B: Using String Manipulation (Legacy/Fallback)
If you are on an older version of Architect that does not support the regex function with capture groups, you can use fixed-position string slicing. This assumes the input is always exactly 12 characters long (+1 + 10 digits).
// Only works if $.input_phone is exactly 12 characters long (e.g., +14155552671)
if (
length($.input_phone) == 12,
"(" + mid($.input_phone, 3, 3) + ") " + mid($.input_phone, 6, 3) + "-" + right($.input_phone, 4),
$.input_phone
)
length($.input_phone): Checks if the string is 12 characters.mid($.input_phone, 3, 3): Extracts 3 characters starting at index 3 (0-indexed). Index 0 is+, 1 is1, 2 is the first digit of the area code. Wait,+1415...- Index 0:
+ - Index 1:
1 - Index 2:
4(Area Code Start) - Index 3:
1 - Index 4:
5 - So,
mid($.input_phone, 2, 3)gets the Area Code. mid($.input_phone, 5, 3)gets the Prefix.right($.input_phone, 4)gets the Line Number.
- Index 0:
Corrected Legacy Expression:
if (
length($.input_phone) == 12,
"(" + mid($.input_phone, 2, 3) + ") " + mid($.input_phone, 5, 3) + "-" + right($.input_phone, 4),
$.input_phone
)
Step 4: Handling Edge Cases
Real-world data is messy. Phone numbers might:
- Have spaces or hyphens already:
+1 415-555-2671 - Be missing the country code:
4155552671 - Be international:
+442079460000
To make the expression robust, we should first sanitize the input.
Sanitization Expression:
Remove all non-digit characters, except the leading +.
// Step 1: Sanitize the input
let sanitized_phone = replace_all(regex("[^\\d+]", $.input_phone, "global"), "")
// Step 2: Format based on sanitized input
if (
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", sanitized_phone) != null,
"(" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", sanitized_phone).group1 + ") " +
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", sanitized_phone).group2 + "-" +
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", sanitized_phone).group3,
sanitized_phone
)
Note: Architect expressions do not support let variables in a single line. You must chain them or use intermediate variables.
Chained Expression (Single Line):
if (
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", replace_all(regex("[^\\d+]", $.input_phone, "global"), "")) != null,
"(" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", replace_all(regex("[^\\d+]", $.input_phone, "global"), "")).group1 + ") " +
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", replace_all(regex("[^\\d+]", $.input_phone, "global"), "")).group2 + "-" +
regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", replace_all(regex("[^\\d+]", $.input_phone, "global"), "")).group3,
replace_all(regex("[^\\d+]", $.input_phone, "global"), "")
)
This is verbose but functional. For readability and maintainability, it is highly recommended to use Intermediate Variables in your Script.
- Action 1: Set Variable
sanitized_phone
Expression:replace_all(regex("[^\\d+]", $.input_phone, "global"), "") - Action 2: Set Variable
formatted_phone
Expression:if ( regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone) != null, "(" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group1 + ") " + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group2 + "-" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group3, $.sanitized_phone )
Complete Working Example
Below is a complete Script definition that can be imported or manually recreated in Architect.
Script Name: Format US Phone Number
Parameters:
input_phone(String): The raw phone number in E.164 or dirty format.
Variables:
sanitized_phone(String)formatted_phone(String)
Actions:
-
Set Variable:
sanitized_phonereplace_all(regex("[^\\d+]", $.input_phone, "global"), "") -
Set Variable:
formatted_phoneif ( regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone) != null, "(" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group1 + ") " + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group2 + "-" + regex("^\\+1(\\d{3})(\\d{3})(\\d{4})$", $.sanitized_phone).group3, $.sanitized_phone ) -
Return Value:
$.formatted_phone
Usage in a Flow
- Drag the Script action into your flow.
- Select
Format US Phone Number. - Map
input_phonetosystem.attributes.phone_numberor your custom variable. - After the script runs, use
$.formatted_phonein your TTS (Text-to-Speech) or SMS message.
Common Errors & Debugging
Error: null result from regex
- Cause: The input phone number does not match the expected pattern. This often happens if the country code is missing or the number is international.
- Fix: Check the
sanitized_phonevariable value in the Architect Debugger. Ensure it starts with+1and has 10 digits following it. If you need to handle international numbers, you must create separate regex patterns for each country code.
Error: regex function not found
- Cause: You are using an older version of the Architect expression engine that does not support the
regexfunction with capture groups. - Fix: Use the Legacy String Manipulation method (Option B in Step 3) using
midandrightfunctions, ensuring the input length is fixed.
Error: Invalid Character in Expression
- Cause: Forgetting to escape backslashes in the regex pattern. In JSON and Architect expressions,
\dmust be written as\\d. - Fix: Replace all single backslashes
\with double backslashes\\in the regex pattern.
Error: Spaces in Output
- Cause: The regex capture groups might include unexpected whitespace if the sanitization step fails.
- Fix: Ensure the sanitization step
replace_all(regex("[^\\d+]", $.input_phone, "global"), "")is executed before the formatting step.