The short version
Every transactional SMS your system sends depends on one critical precondition: the phone number must be formatted correctly before it reaches your provider's API. E.164 is the international standard that defines how phone numbers are represented for global telecommunications, and it's the format every SMS API expects. Mismatched or improperly formatted numbers are among the most common causes of failed deliveries, wasted messages, and support tickets. This guide covers what E.164 actually requires, how to validate and normalize numbers in your code, and the operational patterns that prevent formatting errors from reaching production.
- E.164 format requires a country code (+), area code, and local number with no punctuation
- Validation should happen before API calls, not as a cleanup step after failures
- Libraries like libphonenumber handle edge cases across 200+ countries
- Store numbers in E.164 format internally to avoid repeated normalization
- Invalid numbers should be rejected with clear error messages to upstream systems
What E.164 Actually Requires
E.164 defines a phone number as a maximum of 15 digits, structured as: Country Code (1-3 digits) + National Significant Number (without leading zeros). The number must start with a + prefix when passed to SMS APIs.
For example, a US number (212) 555-1234 becomes +12125551234. A UK number +44 20 7946 0958 becomes +442079460958. The key principle: strip all formatting characters (parentheses, dashes, spaces, plus signs other than the leading one) and prepend the country code.
Common mistakes include keeping the leading zero in national formats (07012345678 instead of +447012345678), omitting the plus sign, or treating all US numbers as 10 digits without the country code. Your SMS provider will reject these with validation errors, but catching them in your own code first prevents unnecessary API calls and provides better error responses to your users.
Implementation Patterns for Production Systems
- Validate at input boundaries: Check format when users submit phone numbers during signup, account changes, or OTP resend requests. Return immediate feedback if the number doesn't match E.164 rules for its claimed country.
- Normalize before storage: Convert numbers to E.164 format when storing in your database. This eliminates repeated normalization logic and ensures every downstream system receives a consistent format.
- Use established libraries: Google's libphonenumber (available in Java, Go, JavaScript, Python, and other languages) handles country detection, number type validation (fixed line, mobile, VoIP), and edge cases better than regex patterns you write yourself.
- Reject VoIP numbers for sensitive flows: If you're sending OTPs or security notifications, validate that the number isn't a VoIP or virtual number. Many SMS providers offer number type detection through their APIs or through separate lookup endpoints.
- Log validation failures: Track which number formats fail and why. This helps identify upstream data quality issues and prevents silent failures where invalid numbers never reach the SMS API.
Operational Checklist for Phone Number Validation
Before sending any transactional SMS, verify that every phone number in your batch meets E.164 format requirements. This single step prevents the majority of delivery failures related to addressing. Integrate validation into your phone number input flows, normalize during storage, and reject malformed numbers with clear error messages. Your support team will thank you, and your delivery rates will remain predictable as you scale OTP and security notification traffic.
Does E.164 work for every country?
Yes, E.164 is the universal standard for international phone numbers and covers all 200+ ITU-member countries. However, some territories share country codes (like +1 for US, Canada, and Caribbean nations), and number portability means the country code doesn't always indicate current location. Use a library like libphonenumber to handle these edge cases.
Should I store E.164 or raw user input?
Store E.164 normalized numbers internally. This ensures consistency across your system and prevents you from having to re-normalize the same number every time you send an SMS. Keep the original user input if needed for audit trails, but use normalized E.164 for all API calls.
What's the difference between validation and normalization?
Validation checks whether a number is potentially valid (correct length, reasonable digits for the country, valid number type). Normalization transforms a number into E.164 format. Both are necessary: validate to reject clearly wrong input, then normalize to ensure API compatibility.
Can I rely on my SMS provider to fix bad number formats?
Some providers attempt basic normalization, but this behavior varies and isn't guaranteed. Relying on provider-side cleanup creates fragility in your system and makes debugging delivery failures harder. Validate and normalize in your own code for predictable behavior.
Read API docs
Use Notilify to build transactional SMS with clearer delivery state, sender planning, and support visibility.
Read API docs