Architect expression regex for E.164 to North American format

Could someone explain why my current regex fails in the Architect expression editor when converting E.164 numbers? I am trying to transform a string like +14155552671 into (415) 555-2671 using the built-in functions. The documentation states: “The replace function allows you to substitute a substring with another value.” I assumed I could use nested replace calls or a simple regex, but the parser rejects complex lookaheads.

I have tried using replace(replace($data.PhoneNumber, "^+1", ""), "(\d{3})(\d{3})(\d{4})", "($1) $2-$3"). This results in a validation error during the flow deployment phase. The error message is generic, stating that the expression is invalid. I expected the capture groups (\d{3}) to work similarly to Python re module groups, but Architect seems to use a different regex flavor.

My FastAPI backend receives the raw E.164 string from the webhook payload. I need to format this within the Architect flow before passing it to an IVR prompt. Can you provide a working expression snippet that handles the +1 prefix removal and the subsequent formatting? I need to ensure the output matches exactly (XXX) XXX-XXXX for compliance with our legacy PBX integration requirements.

You need to abandon the idea of complex regex lookaheads in Architect. The expression engine is strict about character classes and does not support advanced PCRE features like lookarounds. For simple E.164 to North American formatting, you should rely on string manipulation functions rather than a single regex replace.

Here is the robust approach using substring and concat:

  1. Extract digits: Strip the +1 prefix using substring.
  2. Format components: Isolate area code, exchange, and subscriber number.
  3. Concatenate: Reassemble with parentheses and dashes.
// Assuming the input variable is 'phoneNumber'
// Step 1: Remove the '+1' prefix (assuming it's always there)
var cleanNumber = substring(phoneNumber, 2);

// Step 2: Extract parts
var areaCode = substring(cleanNumber, 0, 3);
var exchange = substring(cleanNumber, 3, 3);
var subscriber = substring(cleanNumber, 6, 4);

// Step 3: Format
var formatted = concat("(", areaCode, ") ", exchange, "-", subscriber);

If you must use replace, keep it trivial. The engine chokes on anything resembling a lookahead.

// This works but is brittle if the input format varies
replace(replace(replace(phoneNumber, "+1", ""), substring(phoneNumber, 2, 3), concat("(", substring(phoneNumber, 2, 3), ")")), substring(phoneNumber, 5, 3), concat(substring(phoneNumber, 5, 3), "-"))

The first method is cleaner for debugging. I process millions of these records nightly via the Analytics API and prefer explicit substring logic to avoid regex parsing errors that halt the entire flow. Architect is not a full regex engine; it is a basic string matcher.

Warning: Always validate the input length before slicing. If a number is missing digits, substring will return empty strings or partial data, corrupting your downstream reporting. Add a length() check first.

If you check the docs, they mention the replace function uses basic regex, not full PCRE. I run bulk analytics jobs daily, so I know string parsing fails when you push the engine too hard. The suggestion above using substring is correct but brittle if the input varies.

Here is a more robust method using match to isolate digits first, then format. This avoids lookahead errors.

  • Step 1: Extract only digits using match(data.phoneNumber, "[0-9]+").
  • Step 2: Ensure length is 10 or 11. If 11, drop the leading ‘1’.
  • Step 3: Use substring on the cleaned digit string.
// Clean digits
let digits = match(data.phoneNumber, "[0-9]+");
// Handle country code
if (length(digits) == 11) {
 digits = substring(digits, 1);
}
// Format (XXX) XXX-XXXX
concat("(", substring(digits, 0, 3), ") ", substring(digits, 3, 3), "-", substring(digits, 6, 4))

This works reliably in our production flows. No complex regex needed.