Check numbers are real mobiles before texting
Before a text campaign, confirm each number is valid, put it in E.164 format, and check it is a real mobile line rather than a landline or VoIP number. It runs in two passes: a cheap number-plan check for everyone, then a live carrier check only where it matters.
One command.
Installs into ~/.claude/skills for Claude Code. In Claude, upload the downloaded file under Settings → Capabilities → Skills.
npx ravn skill install check-mobile-before-sms
When to use
- The user is about to send SMS and wants to avoid texting landlines, toll-free or VoIP numbers.
- Phone numbers came in mixed formats from forms or imports.
- "Which of these numbers can actually receive a text?"
Before you start
- Check the connection:
npx ravn whoami(free). phone_to_carrierneeds your own provider key connected — runravn keys add twiliofirst; this tool runs on your own provider account. If it returns 403provider_key_required, stop and tell the human to add their key.validate_phoneis ours and needs no key; it uses the number plan only (carrier_checked: false). Every call is one lookup; misses are free.- Ask which country most numbers are from. National numbers without a
+need acountry(ISO 2-letter code); the default is US. - Confirm the user has consent to text these people. This skill checks numbers, not permission.
Steps
- Pass 1, format and line type from the number plan, for everyone:
npx ravn enrich contacts.csv --tool validate_phone --map phone=Phone,country=Country --dry-run npx ravn enrich contacts.csv --tool validate_phone --map phone=Phone,country=Country --limit 10 --out sample.csv npx ravn enrich contacts.csv --tool validate_phone --map phone=Phone,country=Country --out pass1.csv
If there is no country column, use --column Phone and numbers default to US. Fill empty country cells (for example with US) first, or those rows come back as error with invalid_input. Single number over MCP:
{ "phone": "(415) 555-0134", "country": "US" }
- Sort pass 1 results: -
rw_validfalse orrw_statusnot_found: remove. -rw_line_typelandlineortoll_free: remove from the SMS list. -mobile,voipor unclear: keep for pass 2.
Write the kept rows to keep.csv, renaming rw_phone to phone_e164 and rw_line_type to plan_line_type, and dropping the other pass 1 rw_* columns, so pass 2 does not produce clashing column names. 3. Pass 2, live carrier check, only on the kept rows. Number-plan line type can be wrong when numbers have been ported, so this is the real answer.
npx ravn enrich keep.csv --tool phone_to_carrier --column phone_e164 --dry-run npx ravn enrich keep.csv --tool phone_to_carrier --column phone_e164 --out pass2.csv
The input is the E.164 number from pass 1. The dry run shows how many lookups the pass will use. 4. If phone_to_carrier returns 403 provider_key_required, the account has no carrier provider key connected. Tell the human to add one with ravn keys add twilio, and in the meantime send them the pass 1 result with a clear note that line types are from the number plan only. 5. Keep only rows where the live line_type is mobile. Flag non-fixed VoIP separately; whether to text those is the user's call.
Output
sms-ready.csv with original columns plus:
- From pass 1:
phone_e164andplan_line_type(the number-plan guess). - From pass 2:
rw_status(ok,not_found,error,dropped),rw_phone,rw_line_type(live), the carrier name, andrw_carrier_checkedtrue. sms_ok: yes or no, withsms_reason(invalid, landline, toll_free, voip, mobile).
Also removed.csv and a count per reason.
Rules
- Never text anyone who opted out or replied STOP. Remove them before you start. See /privacy#optout.
- A valid mobile is not consent. Remind the user that texting rules in their country still apply.
- No FCRA uses: do not check numbers to screen people for credit, employment, insurance or housing.
- On a 429
fair_use_exhaustedor a 403provider_key_required, stop and tell the human.
Example prompt
We're texting 2,000 customers a delivery update next week. Phone numbers are all over the place. Make sure we only text real mobiles and fix the formatting.
More skills
Install them all with npx ravn skill install --all.