- Run phone validation on a list (LRN + DNC)
- Build an audience that excludes landlines, VoIP, and unreachable numbers
- Send to the cleaned audience
Why this matters
Carrier networks reject roughly 20–30% of SMS sent to landline / VoIP numbers, and the FTC DNC + state DNC + carrier DNC lists evolve daily. Validating a list once and then segmenting on the result gives you a reusable, deliverable subset — and the cache makes re-validating that same list essentially free forever.Step 1: validate the list
Two ways to trigger validation depending on whether you’re using the platform UI or the public API.From the Contact Lookup UI
- Open Contact Lookup → select your list from the dropdown.
- Click Run phone validation.
- The modal shows a count + cost estimate (e.g., “500 of 9,300 contacts are unvalidated. Estimated cost: $1.50”) and lets you confirm.
- Validation runs as a background job. The progress bar updates every few seconds; you can close the dialog and the job continues server-side.
- When the job completes, the per-contact badges in the list flip to Validated ✓ / Validation failed.
cached: true), so the cost only ever covers the new portion. Validation runs as a single background job per account — clicking again while a job is in flight returns “a validation job is already running” instead of starting a duplicate.
From the public API
For programmatic cleans (e.g., before importing a fresh data drop), usePOST /v1/phone/validate/batch:
So you can pass numbers in whatever format you have them — the
normalize_e164 helper handles the conversion. Numbers that can’t be normalized to E.164 are rejected before any provider call and are never billed: the single-phone validation endpoint returns this as a top-level HTTP 400, while this batch endpoint returns a per-item invalid_phone error inside its results array, with the overall response still 200.
Step 2: build an audience for cleaned, deliverable contacts
Once contacts are validated, theline_type, carrier, and is_valid fields populate on each contact. You can filter on them directly in Audiences.
Example: US/CA mobile-only, no DNC, no VoIP
In the audience builder:
This produces an audience of only fully-validated, mobile-line, US/Canada numbers that aren’t on the federal or per-account DNC list. Every contact in this audience is one your carrier will accept and the recipient can actually receive.
The same audience expressed via the Audiences API:
Variations worth knowing
- Drop landlines but keep VoIP (some VoIP can SMS):
line_type in [mobile, voip] - Carrier-specific A/B: split a campaign by
carrier eq "Verizon Wireless"vscarrier eq "AT&T Mobility"to compare delivery rate / clicks across carriers - Re-clean cadence: validated numbers stay cached for your account permanently, and the dashboard’s Run phone validation only covers contacts that have never been validated. To force a re-validation (e.g., for numbers you suspect have ported), call the validation API with
force=true
Step 3: send to the audience
Schedule a campaign against the audience as you normally would. Because the audience is dynamic, every new validated contact that matches the rules automatically gets included on the next send — no need to re-run the audience query manually.How the cost adds up over time
Validation pricing is $0.003 per non-cached lookup, with a permanent per-account cache (see Phone Validation: Caching Behavior). A typical cycle:
In practice, re-validation costs trend toward “new contacts since last pass × $0.003” rather than total list size.
Common pitfalls
- Trying to send before validation finishes. If you queue a send on a partially-validated list, the as-yet-unvalidated contacts will be sent to under their pre-validation flags (which means landlines/VoIP are NOT excluded). Wait for the badges to flip before scheduling.
- Forgetting
countryin the audience. Without it, the rule still includes UK/AU mobiles where applicable — fine if intentional, but a surprise if you assumed US/CA. - Caching surprises after carrier porting. LRN data is rare-change, but a number ported in the last week may still show its old carrier in our cache. Pass
force=trueif exact current-carrier accuracy matters for your use case.