← Blog

Routing an outbound list by line type

Most teams treat a phone list as one channel. Splitting it on line_type before the first touch costs one pass over the file and changes what every subsequent attempt is worth.

Contents

Most outbound teams treat a phone list as one channel and then wonder why the SMS numbers underperform. A list is at least three channels, and the split is knowable before the first touch.

One pass over the file gives you line_type on every row. What you do with the third bucket is what decides whether the exercise was worth anything.

What line_type actually tells you

POST /v1/verify/phone returns line_type alongside valid, country and the E.164 form. The value comes from Google's libphonenumber metadata: the designated type of the range the number sits in, according to that country's numbering plan.

That is a genuinely strong signal and a bounded one. It tells you what a range is for. It does not tell you what the network currently thinks about one specific number in it — carrier_lookup, ported_status and hlr_liveness sit in checks.not_performed by default, and the response says so on every call.

Where numbering plans separate mobile and fixed ranges — which is most of the world — you get a clean split with no ambiguity to manage.

The three buckets

mobile → SMS is safe. The range is designated for mobile service. This is where a text campaign should live.

fixed_line → calls only. Texting these spends the message and buys nothing. Route them to voice, or drop them if voice is not a channel you run.

fixed_line_or_mobile → its own bucket, with a rule. North America is the hard case: the NANP shares ranges between cell and landline, so the same prefix can be either. The API returns the ambiguous value rather than picking, because picking would be a guess dressed as an answer.

There are also toll_free, voip and premium_rate values worth pulling out — toll-free numbers are switchboards rather than people, and VoIP is disproportionately common in throwaway signups.

The ambiguous bucket is the whole exercise

If you fold fixed_line_or_mobile into mobile, you have re-created the problem you were segmenting to solve, and you have done it invisibly — the rows look routed. If you fold it into fixed, you throw away real mobile numbers.

Two defensible rules:

  1. Route it to voice. Slower per contact, but no wasted messages and no deliverability risk. This is the right default for most teams.
  2. Enrich only that segment. Send hlr:true on those rows and Boundstone runs a live HLR dip at 5 credits on a paid plan. carrier_lookup, ported_status and hlr_liveness move into checks.performed, and you get the current carrier and a reachable value that reads true only when the network answered CONNECTED. If the network cannot resolve the number the dip abstains and the credits are refunded.

Dipping only the ambiguous segment is usually far cheaper than dipping the list, because in most countries that bucket is small.

Doing it over a whole file

POST /v1/bulk/phone takes the file — 250 rows per job on the free tier, 10,000 on a paid plan, one credit per row and refunded on any row that errors. Pull results.csv and segment on the line_type column.

Write e164 back as your canonical phone field while you are there. Deduplicating on formatted numbers is how the same prospect ends up in two sequences.

What this does not settle

Segmentation is hygiene, not compliance. line_type does not tell you whether you are permitted to contact a number — DNC scrubbing and consent are separate steps with separate tools, and nothing here is legal advice.

It also does not tell you the line is live. A perfectly valid mobile can have been disconnected last year and will still segment as mobile, because the numbering plan has not changed. That is what the live dip is for, and what not_performed is telling you when you have not asked for one.

The short version

  • A phone list is at least three channels. mobile to SMS, fixed_line to calls, ambiguous to its own deliberate bucket.
  • line_type is numbering-plan metadata, not a network reading. It describes the range, not today's status of one number in it.
  • Never fold fixed_line_or_mobile into mobile. The API abstained on purpose; treating the abstention as an answer re-creates the problem invisibly.
  • Enrich the ambiguous bucket, not the list. hlr:true at 5 credits, refunded on abstain, applied to a small segment beats dipping everything.
  • Pull toll_free and voip out too. Switchboards are not people, and VoIP skews heavily toward throwaway signups.
  • Segmentation is hygiene, not compliance. DNC and consent remain separate steps.

Frequently asked questions

How do I tell which numbers on my list can receive SMS?

Read line_type on each row. A value of mobile means the number sits in a range its country's numbering plan designates for mobile service, which is the strongest signal you can get without querying the network. A value of fixed_line means calls only. In North America many numbers return fixed_line_or_mobile, because the numbering plan genuinely shares ranges between the two — that is an honest abstention, not a failure, and it needs its own routing rule rather than a guess.

Is line_type a live network check?

No. line_type reads the number's designated type from Google's libphonenumber metadata — what the numbering plan says that range is for, not what the network says about this specific number today. A VoIP line ported onto a mobile range still reads as mobile. Resolving the live answer needs a carrier lookup, which Boundstone runs as a paid opt-in when you send hlr:true.

What should I do with fixed_line_or_mobile numbers?

Give them their own bucket and a deliberate rule, rather than folding them into either side. Texting them is a gamble and calling them is safe but slower, so most outbound teams either route them to voice or enrich just that segment with a live carrier lookup before sending. The important part is that the decision is explicit — the API abstained on purpose, and quietly treating the abstention as mobile is how a segmentation strategy silently stops working.

Thomas Tsui

Founder of Boundstone — building phone, email and IP validation you can actually verify.

One honest API for email, phone and IP — every response lists what it checked and what it didn't claim to. Free tier: 250 credits/month, no card, credits never expire.

More from Boundstone — API documentation · Benchmark methodology · The benchmark series · Buyer's checklist