BongaTech Bulk SMS

Number Normalization & Validation Logic

This chapter details the data cleaning and regex transformation steps executed by the `formatPhoneNumber` utility function. It covers the sanitization layer, international standard routing conversions, and localized format recognition required for carrier compatibility within Kenya.

Telecommunication carriers across East Africa demand a strict structural format for bulk text messaging delivery. Passing raw user input (which often includes white spaces, parentheses, hyphens, and mixed local country prefix conventions) directly to an upstream gateway causes runtime transmission errors or invalid routing dropouts.

To maintain perfect reliability, the Receipt Manager platform passes every recipient string through an isolated cleaning matrix inside the formatPhoneNumber utility.

Technical Specifications & Transformation Flow

The string processing lifecycle applies rules sequentially to convert common user entries into an unhyphenated international format (254XXXXXXXXX):

Phase 1: Pure Digit Extraction

First, the engine applies a global regular expression filter (/\D/g) to strip out everything that isn't a base numeric character. This safely isolates core digits regardless of whether the user typed spaces or prefixes:

  • Input: +254 (712)-345 678 --> Processed: 254712345678
  • Input: 0712-345-678 --> Processed: 0712345678

Phase 2: Standard 10-Digit Local Normalization

If the cleaned sequence has a length of exactly 10 digits and begins with a leading 0, the logic identifies it as standard Kenyan local numbering. It slices away the leading 0 and attaches the standard country routing prefix 254:

  • Input: 0712345678 --> Evaluated length: 10 ──> Processed: 254712345678
  • Input: 0112345678 --> Evaluated length: 10 ──> Processed: 254112345678

Phase 3: Short-Form 9-Digit Local Normalization

Users frequently input phone numbers without both the country prefix and the leading local zero. If a string starts directly with 7 or 1 and measures exactly 9 digits, the system automatically tags it as a valid local number and prepends 254 directly:

  • Input: 712345678 --> Evaluated length: 9 --> Processed: 254712345678
  • Input: 112345678 --> Evaluated length: 9 --> Processed: 254112345678

Implementation Code Reference

The underlying transformation layer is structured as follows:

typescript
export function formatPhoneNumber(phone: string): string {
  // Remove all non-numeric characters
  let cleaned = phone.replace(/\D/g, '');
  
  // If it starts with '0' and is 10 digits long, change '0' to '254'
  if (cleaned.startsWith('0') && cleaned.length === 10) {
    cleaned = '254' + cleaned.substring(1);
  }
  
  // If it starts with '7' or '1' and is 9 digits long (local format without leading 0)
  if ((cleaned.startsWith('7') || cleaned.startsWith('1')) && cleaned.length === 9) {
    cleaned = '254' + cleaned;
  }
  
  return cleaned;
}

Unit Testing Evaluation Matrix

When integrating or verifying your platform implementation, verify that individual inputs resolve correctly against this mapping matrix:

Raw Input ValueRegex CleanedNormalized Output ResultRule Path Triggered
0712345678071234567825471234567810-Digit Leading Zero Rule
0112345678011234567825411234567810-Digit Leading Zero Rule
7123456787123456782547123456789-Digit Short-Form Rule
+254 712 345 678254712345678254712345678Direct Return (No Match Pass-through)
254112345678254112345678254112345678Direct Return (No Match Pass-through)

Pre-flight Integrity Interception

Before sending the data over HTTP, the parent processing pipeline evaluates the resulting value. If the consumer submits an entirely non-numeric sequence (like "John Doe"), the string evaluates to an empty value. The module halts operations immediately and flags an error without wasting API credits:

json
{
  "success": false,
  "error": "Invalid phone number: Number contains no digits."
}
Last updated: 7/16/2026