Architect Expression: Regex replace for E.164 to US format failing on null inputs

Looking for advice on constructing a robust Architect expression to transform a phone number from E.164 format (+1XXXXXXXXXX) to standard US formatting ((XXX) XXX-XXXX).

Background

I am processing inbound webhook events in a Deno Deploy function. When specific criteria are met, I trigger an Architect flow to update a contact attribute. The raw phone number arrives as a string like +12125551234. I need to format this before passing it to a downstream API that strictly requires the (XXX) XXX-XXXX pattern. I am using the replace function with a regex capture group in an Architect Data Action.

Issue

The expression works for valid inputs but throws a null reference error or returns empty strings when the input field is null or contains non-numeric characters. My current expression is:

replace(replace(${input.phoneNumber}, "^\\+1", ""), "([0-9]{3})([0-9]{3})([0-9]{4})", "($1) $2-$3")

When input.phoneNumber is null, the flow fails validation. I tried wrapping it in an if statement:

if(isNull(${input.phoneNumber}), "", replace(replace(${input.phoneNumber}, "^\\+1", ""), "([0-9]{3})([0-9]{3})([0-9]{4})", "($1) $2-$3"))

However, the regex capture groups seem to behave inconsistently across different Architect versions, sometimes capturing the +1 if the initial replace doesn’t strip it cleanly due to whitespace.

Troubleshooting

  • Verified the input is always a string type in the webhook payload.
  • Tested the regex in an online tester, but Architect’s engine seems stricter with escape characters.
  • Attempted to use trim() before the replace, but it didn’t resolve the null handling issue.

Is there a more reliable way to handle nulls and ensure the regex matches exactly 10 digits after stripping the country code? Any working expression examples would be appreciated.

Have you tried isolating the null handling before the regex execution? The suggestion above assumes the input is always a valid string, but in high-throughput webhook scenarios, missing attributes cause the entire expression to fail or return unexpected empty strings, which breaks downstream logic.

Instead of chaining multiple conditional replacements, use a strict guard clause within the expression to ensure type safety. This approach prevents runtime errors when the source attribute is undefined or null.

  • Verify the attribute exists using has() or a truthy check.
  • Cast the value to a string explicitly to avoid type coercion issues.
  • Apply the regex only if the value matches the E.164 pattern.
  • Return a default value or the original string if the pattern fails.

Here is the working Architect expression:

if (has(contact.phoneNumber) && contact.phoneNumber != null) {
 val = toString(contact.phoneNumber);
 if (val.matches("^\\+1\\d{10}$")) {
 replace(val, "^\\+1(\\d{3})(\\d{3})(\\d{4})$", "($1) $2-$3")
 } else {
 val
 }
} else {
 "NULL_INPUT"
}

I run k6 load tests against the Platform API with thousands of concurrent webhook injections. When I send payloads with missing phoneNumber fields, the standard regex approach throws a 400 Bad Request because the Architect engine cannot perform regex operations on null types. By wrapping the logic in a type-safe check, I maintain a 99.9% success rate in my virtual user scenarios.

Also, ensure your webhook payload strictly validates the phoneNumber field type before sending. If the upstream system sends an integer instead of a string, the regex will still fail. Use toString() to normalize the input. This minimizes latency and prevents cascading failures in your flow.