twilio-lookup
Phone intelligence: number validation, carrier lookup, caller ID, line type mobile/landline/VoIP, CNAM
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o twilio-lookup.zip https://jpskill.com/download/22288.zip && unzip -o twilio-lookup.zip && rm twilio-lookup.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/22288.zip -OutFile "$d\twilio-lookup.zip"; Expand-Archive "$d\twilio-lookup.zip" -DestinationPath $d -Force; ri "$d\twilio-lookup.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
twilio-lookup.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
twilio-lookupフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
[スキル名] twilio-lookup
twilio-lookup
目的
twilio-lookup は、Twilio Lookup を使用して、電話番号の検証、E.164 形式への正規化、キャリアメタデータ、回線種別(携帯電話/固定電話/VoIP)、発信者ID/CNAM(サポートされている場合)の取得など、本番環境レベルの電話インテリジェンスワークフローを可能にします。エンジニアはこれを利用して、以下のことを行います。
- SMS/WhatsApp/Voice を送信する前に、無効な番号や到達不能な番号を拒否します(21211/30003 エラーとコストを削減します)。
- 回線種別によってトラフィックをルーティングします(例:固定電話への SMS を回避する、VoIP が多い地域では Voice フォールバックを選択する)。
- 地域/国ポリシーを適用します(例:高リスク国をブロックする、10DLC には米国/カナダを必須とする)。
- 不正スコアリングやサポートツール用に、キャリアと発信者名でユーザープロファイルを充実させます。
- サービス全体で決定論的な正規化パイプライン(E.164 + 国コード)を構築します。
このガイドは、Lookup を、堅牢なエラー処理、レート制限、セキュリティ制御を備えた、より大規模な Twilio 本番スタック(Messaging/Voice/Verify/Studio/SendGrid)に統合することを前提としています。
前提条件
アカウントと API 認証情報
- Twilio アカウント SID(形式
ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) - Twilio 認証トークン
- オプション:Twilio API キー + シークレット(本番環境では認証トークンよりも推奨されます)
- API キー SID 形式
SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- API キー SID 形式
API キーの作成方法:
- Twilio Console → Account → API keys & tokens → Create API key
- シークレットはシークレットマネージャー(AWS Secrets Manager、GCP Secret Manager、Vault)に保存してください。コミットしないでください。
Twilio Lookup API
- Lookup API v2 が現在の推奨 API サーフェスです。
- 一部のフィールド(例:発信者名)はアドオンが必要な場合や、地域に依存する場合があります。
ランタイムバージョン(テスト済み)
- Node.js: 20.11.1 (LTS)
- Python: 3.11.8
- Java: 17.0.10 (Temurin)
- Go: 1.22.1
- Twilio ヘルパーライブラリ:
- twilio-node: 4.23.0
- twilio-python: 9.4.1
- Twilio CLI(オプションですが便利です): 5.19.0
OS サポート
- Ubuntu 22.04 LTS (x86_64)
- Fedora 39 (x86_64)
- macOS 14 Sonoma (Intel + Apple Silicon)
ネットワークと TLS
lookups.twilio.comへのアウトバウンド HTTPS(および認証/その他のサービス用のapi.twilio.com)。- TLS インスペクションプロキシは SNI と最新の暗号を許可する必要があります。そうでない場合、断続的な
ECONNRESET/ハンドシェイク失敗が発生する可能性があります。
コアコンセプト
メンタルモデル:「正規化 → 充実 → 決定 → 実行」
- 正規化: ユーザー入力を E.164 形式(例:
+14155552671)に変換し、妥当性を検証します。 - 充実: メタデータ(キャリア、回線種別、発信者名)を取得します。
- 決定: ポリシーを適用します(許可/拒否、SMS と Voice へのルーティング、Verify の要求)。
- 実行: メッセージ/通話/検証を送信します。監査と将来のルーティングのために充実したデータを保存します。
Lookup API エンティティ
- 電話番号: 入力はローカル/国内形式でも可能ですが、出力は E.164 形式であるべきです。
- フィールド: コスト/レイテンシを増加させるオプションの拡張です。
- 一般的なフィールド:
line_type_intelligence(回線種別 + キャリア)caller_name(利用可能な場合の CNAM に似たデータ)
- 一般的なフィールド:
- 国コード: 入力が E.164 形式でない場合に解析に影響を与えます。
アーキテクチャの概要
一般的な本番フロー:
- エッジ/API が
phone文字列を受信します。 - サービスが Lookup を使用して正規化と検証を行います。
- 結果はユーザープロファイルテーブルに以下の情報とともに保存されます。
e164、country_code、national_formatline_type、carrier_name、mobile_country_code、mobile_network_codecaller_name(使用した場合)lookup_timestamp、lookup_version、risk_flags
- ダウンストリーム:
- メッセージングサービスは、地域に一致する Messaging Service SID を選択します。
- Verify は正規化された E.164 を使用します。
- Voice は E.164 とオプションで SIP ルーティングを使用します。
コストとレイテンシのトレードオフ
- 基本的な番号検証は、追加フィールドを要求するよりも安価です。
fields=line_type_intelligenceは追加のルックアップ作業を伴うため、結果をキャッシュしてください。fields=caller_nameは遅く、信頼性が低い場合があります。必要な場合にのみ使用してください。
キャッシュ戦略
- リスク許容度に応じて、E.164 ごとに TTL(例:7~30日)でキャッシュします。
- キャリアと回線種別は変更される可能性があります(番号ポータビリティ)。高リスクのフローでは、重要なイベント(パスワードリセット、支払い)で再確認してください。
インストールとセットアップ
公式 Python SDK — Lookup
リポジトリ: https://github.com/twilio/twilio-python
PyPI: pip install twilio · サポート: Python 3.7–3.13
from twilio.rest import Client
client = Client()
# Basic lookup
phone = client.lookups.v2.phone_numbers("+15558675309").fetch()
print(phone.country_code, phone.phone_number)
# With add-ons
phone = client.lookups.v2.phone_numbers("+15558675309").fetch(
fields=["line_type_intelligence", "caller_name", "sim_swap"]
)
print(phone.line_type_intelligence) # {"type": "mobile", "error_code": null}
print(phone.caller_name) # {"caller_name": "Alice", "error_code": null}
出典: twilio/twilio-python — lookups
1) Twilio CLI をインストールする(オプションですが推奨)
Ubuntu 22.04 (x86_64)
curl -sSL https://twilio-cli-prod.s3.amazonaws.com/twilio-cli-linux-x86_64.tar.gz -o /tmp/twilio.tar.gz
sudo tar -xzf /tmp/twilio.tar.gz -C /usr/local/bin twilio
twilio --version
Fedora 39 (x86_64)
curl -sSL https://twilio-cli-prod.s3.amazonaws.com/twilio-cli-linux-x86_64.tar.gz -o /tmp/twilio.tar.gz
sudo tar -xzf /tmp/twilio.tar.gz -C /usr/local/bin twilio
twilio --version
macOS (Intel + Apple Silicon) via Homebrew
brew update
brew install twilio/brew/twilio
twilio --version
2) Twilio CLI を認証する
推奨: API キー
twilio login --apikey YOUR_API_KEY_SID --apisecret 'your_api_key_secret' --profile prod
twilio profiles:list
代替: アカウント SID + 認証トークン
twilio login --sid YOUR_ACCOUNT_SID --token 'your_auth_token' --profile prod
3) ヘルパーライブラリをインストールする
Node.js (20.11.1)
node --version
npm --version
mkdir -p twilio-lookup-demo-node && cd twilio-lookup-demo-node
npm init -y
npm install twilio@4.23.0 pino@9.0.0
Python (3.11.8)
python3 --version
python3 -m ve 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
twilio-lookup
Purpose
twilio-lookup enables production-grade phone intelligence workflows using Twilio Lookup: validating phone numbers, normalizing to E.164, retrieving carrier metadata, line type (mobile/landline/VoIP), and caller ID / CNAM (where supported). Engineers use this to:
- Reject invalid or unreachable numbers before sending SMS/WhatsApp/Voice (reduce 21211/30003 failures and cost).
- Route traffic by line type (e.g., avoid SMS to landlines; choose Voice fallback for VoIP-heavy regions).
- Enforce geo/region policy (e.g., block high-risk countries, require US/CA for 10DLC).
- Enrich user profiles with carrier + caller name for fraud scoring and support tooling.
- Build deterministic normalization pipelines (E.164 + country code) across services.
This guide assumes you are integrating Lookup into a larger Twilio production stack (Messaging/Voice/Verify/Studio/SendGrid), with strong error handling, rate limiting, and security controls.
Prerequisites
Accounts & API credentials
- Twilio Account SID (format
ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) - Twilio Auth Token
- Optional: Twilio API Key + Secret (recommended over Auth Token for production)
- API Key SID format
SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- API Key SID format
Create API Key:
- Twilio Console → Account → API keys & tokens → Create API key
- Store secrets in a secret manager (AWS Secrets Manager, GCP Secret Manager, Vault). Do not commit.
Twilio Lookup API
- Lookup API v2 is the current recommended API surface.
- Some fields (e.g., caller name) may require add-ons or are region-dependent.
Runtime versions (tested)
- Node.js: 20.11.1 (LTS)
- Python: 3.11.8
- Java: 17.0.10 (Temurin)
- Go: 1.22.1
- Twilio helper libraries:
- twilio-node: 4.23.0
- twilio-python: 9.4.1
- Twilio CLI (optional but useful): 5.19.0
OS support
- Ubuntu 22.04 LTS (x86_64)
- Fedora 39 (x86_64)
- macOS 14 Sonoma (Intel + Apple Silicon)
Network & TLS
- Outbound HTTPS to
lookups.twilio.com(andapi.twilio.comfor auth/other services). - TLS inspection proxies must allow SNI and modern ciphers; otherwise expect intermittent
ECONNRESET/handshake failures.
Core Concepts
Mental model: “Normalize → Enrich → Decide → Act”
- Normalize: Convert user input to E.164 (e.g.,
+14155552671) and validate plausibility. - Enrich: Fetch metadata (carrier, line type, caller name).
- Decide: Apply policy (allow/deny, route to SMS vs Voice, require Verify).
- Act: Send message/call/verification; store enrichment for audit and future routing.
Lookup API entities
- Phone number: input can be local/national; output should be E.164.
- Fields: optional expansions that increase cost/latency.
- Common fields:
line_type_intelligence(line type + carrier)caller_name(CNAM-like data where available)
- Common fields:
- Country code: influences parsing when input is not E.164.
Architecture overview
Typical production flow:
- Edge/API receives
phonestring. - Service normalizes and validates using Lookup.
- Result stored in a user profile table with:
e164,country_code,national_formatline_type,carrier_name,mobile_country_code,mobile_network_codecaller_name(if used)lookup_timestamp,lookup_version,risk_flags
- Downstream:
- Messaging service chooses Messaging Service SID with geo-matching.
- Verify uses normalized E.164.
- Voice uses E.164 and optionally SIP routing.
Cost and latency tradeoffs
- Basic number validation is cheaper than requesting additional fields.
fields=line_type_intelligenceadds extra lookup work; cache results.fields=caller_namecan be slower and less reliable; use only when needed.
Caching strategy
- Cache by E.164 for a TTL (e.g., 7–30 days) depending on your risk tolerance.
- Carrier and line type can change (porting). For high-risk flows, re-check on critical events (password reset, payout).
Installation & Setup
Official Python SDK — Lookup
Repository: https://github.com/twilio/twilio-python
PyPI: pip install twilio · Supported: Python 3.7–3.13
from twilio.rest import Client
client = Client()
# Basic lookup
phone = client.lookups.v2.phone_numbers("+15558675309").fetch()
print(phone.country_code, phone.phone_number)
# With add-ons
phone = client.lookups.v2.phone_numbers("+15558675309").fetch(
fields=["line_type_intelligence", "caller_name", "sim_swap"]
)
print(phone.line_type_intelligence) # {"type": "mobile", "error_code": null}
print(phone.caller_name) # {"caller_name": "Alice", "error_code": null}
Source: twilio/twilio-python — lookups
1) Install Twilio CLI (optional but recommended)
Ubuntu 22.04 (x86_64)
curl -sSL https://twilio-cli-prod.s3.amazonaws.com/twilio-cli-linux-x86_64.tar.gz -o /tmp/twilio.tar.gz
sudo tar -xzf /tmp/twilio.tar.gz -C /usr/local/bin twilio
twilio --version
Fedora 39 (x86_64)
curl -sSL https://twilio-cli-prod.s3.amazonaws.com/twilio-cli-linux-x86_64.tar.gz -o /tmp/twilio.tar.gz
sudo tar -xzf /tmp/twilio.tar.gz -C /usr/local/bin twilio
twilio --version
macOS (Intel + Apple Silicon) via Homebrew
brew update
brew install twilio/brew/twilio
twilio --version
2) Authenticate Twilio CLI
Preferred: API Key
twilio login --apikey YOUR_API_KEY_SID --apisecret 'your_api_key_secret' --profile prod
twilio profiles:list
Alternative: Account SID + Auth Token
twilio login --sid YOUR_ACCOUNT_SID --token 'your_auth_token' --profile prod
3) Install helper libraries
Node.js (20.11.1)
node --version
npm --version
mkdir -p twilio-lookup-demo-node && cd twilio-lookup-demo-node
npm init -y
npm install twilio@4.23.0 pino@9.0.0
Python (3.11.8)
python3 --version
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip==24.0
pip install twilio==9.4.1 httpx==0.27.0
4) Environment variables
Set in your shell (dev) or secret manager (prod):
export TWILIO_ACCOUNT_SID="YOUR_ACCOUNT_SID"
export TWILIO_AUTH_TOKEN="your_auth_token"
# or API key auth:
export TWILIO_API_KEY_SID="YOUR_API_KEY_SID"
export TWILIO_API_KEY_SECRET="your_api_key_secret"
5) Verify connectivity
curl -sS https://lookups.twilio.com/ -I | head
Expect HTTP/2 404 or similar (root path not found is fine); the goal is TLS connectivity.
Key Capabilities
Number validation + E.164 normalization
- Accept user input in various formats.
- Use
countryCodewhen input is not E.164. - Persist normalized E.164 and derived country.
Key output fields:
phone_number(E.164)national_formatcountry_codevalid(v1) / v2 semantics vary; treat as “lookup succeeded” + parse results.
Carrier lookup + line type intelligence
Use fields=line_type_intelligence to retrieve:
line_type(e.g.,mobile,landline,voip,nonFixedVoip,tollFree)carrier_namemobile_country_code(MCC)mobile_network_code(MNC)error_code(carrier-specific issues)
Production uses:
- Route SMS only to
mobile/nonFixedVoipdepending on policy. - Flag
voipfor fraud scoring (common in account takeovers). - Detect toll-free and apply different messaging rules.
Caller ID / CNAM (caller_name)
Use fields=caller_name (availability varies by country and number type).
Typical use:
- Support tooling (display “likely name”).
- Fraud heuristics (mismatch between claimed name and CNAM).
Do not treat CNAM as authoritative identity.
Policy enforcement (geo + risk)
Combine Lookup results with:
- Country allow/deny lists
- Known high-risk carriers
- VoIP restrictions for sensitive actions
- 10DLC constraints (US A2P messaging requires registration; Lookup helps ensure US numbers are actually US)
Pre-send gating for Messaging/Voice/Verify
- Messaging: reduce
21211invalid To and30003unreachable. - Voice: avoid calling invalid numbers; choose SIP trunk vs PSTN.
- Verify: normalize E.164; block VoIP if required by policy.
Command Reference
Twilio CLI: Lookup (if installed)
Twilio CLI command names can vary by version/plugins. In CLI 5.19.0, Lookup is typically available via the api surface.
Fetch a phone number (basic)
twilio api:lookups:v2:phone-numbers:fetch --phone-number "+14155552671" --profile prod
Flags:
--phone-number(string, required): E.164 or raw input.--profile(string): Twilio CLI profile name.
Fetch with fields
twilio api:lookups:v2:phone-numbers:fetch \
--phone-number "+14155552671" \
--fields "line_type_intelligence,caller_name" \
--profile prod
Flags:
--fields(comma-separated):line_type_intelligence,caller_name
Fetch with country code (for non-E.164 input)
twilio api:lookups:v2:phone-numbers:fetch \
--phone-number "4155552671" \
--country-code "US" \
--fields "line_type_intelligence" \
--profile prod
Flags:
--country-code(ISO-3166-1 alpha-2): e.g.,US,CA,GB
If your CLI build does not expose api:lookups:*, use direct HTTPS (below) or helper libraries.
Direct HTTPS (curl) — Lookup v2
Twilio Lookup v2 endpoint pattern:
GET https://lookups.twilio.com/v2/PhoneNumbers/{PhoneNumber}?Fields=...&CountryCode=...
Basic lookup
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"https://lookups.twilio.com/v2/PhoneNumbers/+14155552671" | jq
With fields
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"https://lookups.twilio.com/v2/PhoneNumbers/+14155552671?Fields=line_type_intelligence,caller_name" | jq
With CountryCode for national input
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"https://lookups.twilio.com/v2/PhoneNumbers/4155552671?CountryCode=US&Fields=line_type_intelligence" | jq
Notes:
- Use URL encoding for
+if your tooling mishandles it (%2B14155552671). - Prefer API Key auth in production by using
SK...:secretbasic auth.
Node.js (twilio-node 4.23.0)
Basic lookup
import twilio from "twilio";
const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const res = await client.lookups.v2
.phoneNumbers("+14155552671")
.fetch();
console.log(res.phoneNumber, res.countryCode, res.nationalFormat);
With fields
import twilio from "twilio";
const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const res = await client.lookups.v2
.phoneNumbers("+14155552671")
.fetch({ fields: "line_type_intelligence,caller_name" });
console.log({
e164: res.phoneNumber,
lineType: res.lineTypeIntelligence?.type,
carrier: res.lineTypeIntelligence?.carrier_name,
callerName: res.callerName?.caller_name,
});
Flags/options:
fetch({ fields: "..." }): comma-separated fieldsfetch({ countryCode: "US" }): for national input (if supported by helper version)
API Key auth (recommended)
import twilio from "twilio";
const client = twilio(
process.env.TWILIO_API_KEY_SID,
process.env.TWILIO_API_KEY_SECRET,
{ accountSid: process.env.TWILIO_ACCOUNT_SID }
);
Python (twilio-python 9.4.1)
Basic lookup
import os
from twilio.rest import Client
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
res = client.lookups.v2.phone_numbers("+14155552671").fetch()
print(res.phone_number, res.country_code, res.national_format)
With fields
import os
from twilio.rest import Client
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
res = client.lookups.v2.phone_numbers("+14155552671").fetch(
fields="line_type_intelligence,caller_name"
)
print({
"e164": res.phone_number,
"line_type": getattr(res, "line_type_intelligence", None),
"caller_name": getattr(res, "caller_name", None),
})
API Key auth
import os
from twilio.rest import Client
client = Client(
os.environ["TWILIO_API_KEY_SID"],
os.environ["TWILIO_API_KEY_SECRET"],
os.environ["TWILIO_ACCOUNT_SID"],
)
Configuration Reference
1) Service configuration file (recommended)
Path (Linux):
/etc/openclaw/twilio/lookup.toml
Path (macOS dev):
~/Library/Application Support/OpenClaw/twilio/lookup.toml
Example:
# /etc/openclaw/twilio/lookup.toml
[twilio]
account_sid = "YOUR_ACCOUNT_SID"
auth_mode = "api_key" # "api_key" or "auth_token"
[twilio.api_key]
sid = "YOUR_API_KEY_SID"
secret_env = "TWILIO_API_KEY_SECRET" # secret is read from env at runtime
[lookup]
base_url = "https://lookups.twilio.com"
default_country_code = "US"
default_fields = ["line_type_intelligence"]
timeout_ms = 2500
max_retries = 2
retry_backoff_ms = 200
retry_jitter_ms = 100
[cache]
enabled = true
backend = "redis" # "redis" or "memory"
ttl_seconds = 604800 # 7 days
negative_ttl_seconds = 3600 # cache invalid numbers for 1 hour
[policy]
allow_countries = ["US", "CA"]
deny_line_types = ["landline"]
flag_line_types = ["voip", "nonFixedVoip"]
2) Environment variables
Production runtime should support:
TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN(if using auth token)TWILIO_API_KEY_SIDTWILIO_API_KEY_SECRETOPENCLAW_TWILIO_LOOKUP_CONFIG=/etc/openclaw/twilio/lookup.toml
Example systemd drop-in:
# /etc/systemd/system/openclaw.service.d/twilio-lookup.conf
[Service]
Environment="OPENCLAW_TWILIO_LOOKUP_CONFIG=/etc/openclaw/twilio/lookup.toml"
Environment="TWILIO_ACCOUNT_SID=YOUR_ACCOUNT_SID"
Environment="TWILIO_API_KEY_SID=YOUR_API_KEY_SID"
Environment="TWILIO_API_KEY_SECRET=/run/secrets/twilio_api_key_secret"
If TWILIO_API_KEY_SECRET points to a file path, your runtime should read file contents (common pattern). If not supported, store the secret directly in the env var via your secret injector.
3) Redis cache configuration (if used)
Path:
/etc/openclaw/redis.conf.d/lookup.conf
# /etc/openclaw/redis.conf.d/lookup.conf
maxmemory 512mb
maxmemory-policy allkeys-lru
timeout 0
tcp-keepalive 300
Key format recommendation:
lookup:v2:{e164}:{fields_hash}:{country_code}
Integration Patterns
Pattern: Pre-send gating for Messaging (SMS/WhatsApp)
Pipeline:
- Lookup normalize + line type
- If invalid → reject request (HTTP 400)
- If landline → route to Voice or block
- If mobile/voip → send via Messaging Service (geo-match)
- Track message status webhooks; handle STOP
Example (pseudo):
POST /send_sms
-> lookup(+input, fields=line_type_intelligence)
-> if line_type in deny: 422 "unsupported line type"
-> send SMS via Messaging Service SID MGxxxxxxxx...
-> store message SID + lookup snapshot
Compose with Twilio Messaging production patterns:
- Use Messaging Service with geo-matching to reduce cost and improve deliverability.
- Handle webhook retries and idempotency for status callbacks.
- Enforce opt-out: STOP/START/HELP keywords.
Pattern: Verify enrollment with VoIP restriction
- Lookup first; if
voipand your fraud policy disallows VoIP for MFA, require alternate channel (TOTP/email) via Verify custom channels.
Pattern: Voice fallback for landlines
- If Lookup returns
landline, skip SMS and place a Voice call using TwiML<Say>with Polly voice, or<Dial>to connect.
Pattern: Studio Flow trigger with enrichment
- Use Lookup in your backend, then trigger Studio Flow via REST Trigger API with enriched parameters (
line_type,carrier,country). - Studio can branch on these parameters for IVR/SMS flows.
Pattern: Data warehouse enrichment job
- Batch job reads new/changed phone numbers, runs Lookup with rate limiting, writes results to warehouse.
- Use caching and backoff to avoid
20429 Too Many Requests.
Error Handling & Troubleshooting
Handle errors as first-class: classify, retry safely, and surface actionable diagnostics.
1) Twilio error 20003 — authentication
Message (common):
Authenticate- Or JSON:
"code": 20003, "message": "Authenticate"
Root causes:
- Wrong Auth Token / API Key secret
- Using API Key SID without
accountSidin helper client - Secret rotated but not deployed
Fix:
- Verify credentials in secret manager and runtime injection.
- For API Key auth in helper libs, set
accountSid. - Confirm you are hitting
lookups.twilio.comwith correct basic auth.
2) Twilio error 20429 — rate limiting
Message:
"code": 20429, "message": "Too Many Requests"
Root causes:
- Burst traffic without client-side throttling
- Batch enrichment job too aggressive
Fix:
- Implement token bucket per account (and per region if needed).
- Exponential backoff with jitter; cap retries (e.g., 2–3).
- Cache results; avoid repeated lookups for same E.164.
3) Twilio error 21211 — invalid ‘To’ (downstream Messaging/Voice)
Message:
The 'To' number +1415555 is not a valid phone number.
Root causes:
- Skipping Lookup normalization
- Accepting user input without country context
- Storing non-E.164 and reusing later
Fix:
- Always store E.164 from Lookup.
- Require
countryCodefor national inputs. - Add validation at API boundary; reject ambiguous formats.
4) Twilio error 30003 — Unreachable destination handset (Messaging)
Message:
Unreachable destination handset
Root causes:
- Number is valid but not reachable (inactive, roaming restrictions, blocked)
- Carrier filtering / destination restrictions
Fix:
- Use Lookup line type + carrier to detect problematic segments.
- Implement fallback channels (WhatsApp, Voice, email).
- Track delivery failures and suppress repeated sends.
5) HTTP 400 from Lookup — malformed request
Message (typical):
400 Bad Request
Root causes:
- Invalid
Fieldsvalue (typo) - Bad URL encoding of
+in phone number - Unsupported
CountryCodeformat (must beUS, notUSA)
Fix:
- Validate
fieldsagainst allowlist:line_type_intelligence,caller_name. - URL-encode phone numbers in raw HTTP clients.
- Enforce ISO alpha-2 country codes.
6) HTTP 404 from Lookup — number not found / invalid
Message (common JSON):
"status": 404, "message": "The requested resource /PhoneNumbers/... was not found"
Root causes:
- Number is not parseable/valid for the given country context
- Missing
CountryCodefor national format input
Fix:
- Retry once with explicit
CountryCodeif you have user locale. - Otherwise reject and request E.164.
7) Network errors: timeouts / TLS
Messages:
- Node:
ETIMEDOUT,ECONNRESET - Python httpx:
ReadTimeout,ConnectError
Root causes:
- Egress firewall/proxy issues
- Too-low timeout under load
- DNS instability
Fix:
- Set timeout ~2–3s for Lookup; do not block critical paths indefinitely.
- Use retries only for network errors (not 4xx).
- Ensure DNS resolvers are stable; consider caching DNS at OS level.
8) JSON parsing / schema drift
Message:
TypeError: Cannot read properties of undefined (reading 'type')- Or Python:
AttributeError: 'PhoneNumberInstance' object has no attribute ...
Root causes:
- Assuming fields exist when not requested
- Region does not support caller_name
- Helper library version mismatch
Fix:
- Treat optional expansions as nullable.
- Gate access:
res.lineTypeIntelligence?.type. - Pin helper library versions; add contract tests.
9) Twilio error 21614 / 21610 (opt-out) — downstream Messaging
Message:
21610: Message cannot be sent because the recipient has opted out of receiving messages from this number.21614: 'To' number is not a valid mobile number
Root causes:
- Not honoring STOP lists
- Sending SMS to landline
Fix:
- Maintain opt-out suppression lists; handle inbound STOP/START.
- Use Lookup line type gating before sending.
Security Hardening
Credential handling
- Prefer API Key + Secret over Auth Token.
- Rotate API keys quarterly (or per policy).
- Store secrets in a secret manager; inject at runtime.
- Ensure logs never include:
- Auth Token / API key secret
- Full phone numbers in plaintext (mask or hash)
Masking recommendation:
- Log last 2–4 digits only:
+1415555**** - Or store SHA-256 of E.164 for correlation.
Least privilege and blast radius
- Use separate Twilio subaccounts for environments (dev/stage/prod).
- Use separate API keys per service (lookup-enricher vs messaging-sender).
- If using Twilio Console roles, restrict who can create/rotate keys.
Transport security
- Enforce TLS 1.2+.
- If using corporate proxies, pin allowed domains:
lookups.twilio.comapi.twilio.com
CIS-aligned host hardening (where applicable)
- CIS Ubuntu Linux 22.04 LTS Benchmark (v1.0.0) relevant controls:
- Ensure secrets are not world-readable (
chmod 600for config files with SIDs). - Restrict outbound traffic to required destinations (egress allowlist).
- Centralize audit logs; protect log integrity.
- Ensure secrets are not world-readable (
Webhook security (composed systems)
Lookup itself is outbound-only, but it composes with Messaging/Voice webhooks:
- Validate Twilio signatures (
X-Twilio-Signature) on status callbacks. - Implement idempotency keys for webhook processing.
- Apply retry-safe handlers (Twilio retries on non-2xx).
Performance Tuning
1) Cache Lookup results (largest win)
Expected impact:
- 60–95% reduction in Lookup calls in steady state (depending on churn).
- Lower p95 latency for user-facing endpoints (cache hit ~1–5ms vs network 150–600ms).
Guidelines:
- TTL 7 days for general routing.
- TTL 1 day for high-risk flows if porting risk matters.
- Negative cache invalid numbers for 1 hour to reduce repeated abuse.
2) Request only required fields
Expected impact:
- Lower cost and latency.
- Avoid
caller_nameunless needed.
Example:
- Signup:
line_type_intelligenceonly. - Support agent view:
caller_nameon-demand.
3) Concurrency control + backpressure
- Implement a per-process semaphore (e.g., max 50 concurrent lookups).
- Add a global rate limiter (Redis-based) for batch jobs.
Expected impact:
- Fewer
20429responses. - More predictable latency under load.
4) Timeouts and retries
Recommended:
- Timeout: 2.5s
- Retries: 2 (network/5xx/20429 only)
- Backoff: 200ms, 400ms + jitter
Expected impact:
- Reduced tail latency amplification.
- Avoid retry storms.
Advanced Topics
Porting and carrier drift
- Carrier name and line type can change due to number porting.
- For critical actions (payouts, password resets), re-check Lookup if cached data is older than a threshold (e.g., 24h).
Handling ambiguous national numbers
- If user enters
07700 900123, you need country context (GB). - Use:
- User-selected country
- GeoIP-derived country (with caution)
- Account profile country
- If you guess wrong, you may normalize incorrectly; prefer explicit country selection in UX.
VoIP nuance
- Some providers classify as
nonFixedVoipvsvoip. - Decide policy explicitly:
- Allow
nonFixedVoipfor low-risk notifications - Block for MFA if your threat model requires it
- Allow
Toll-free and short codes
- Lookup is for phone numbers; short codes are not standard E.164.
- For messaging, treat short codes separately; do not send them to Lookup.
Internationalization and formatting
- Store E.164 as canonical.
- Store
national_formatfor display only; do not use it for sending.
Data retention
- Phone intelligence can be considered personal data.
- Apply retention policies:
- Keep only what you need (line type, country) and for as long as needed.
- Consider hashing E.164 for analytics.
Usage Examples
1) API endpoint: validate + normalize + store
Node.js example with strict policy and caching stub:
import twilio from "twilio";
import pino from "pino";
const log = pino();
const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const ALLOW_COUNTRIES = new Set(["US", "CA"]);
const DENY_LINE_TYPES = new Set(["landline"]);
export async function normalizePhone({ input, countryCode }) {
const res = await client.lookups.v2
.phoneNumbers(input)
.fetch({ fields: "line_type_intelligence", countryCode });
const e164 = res.phoneNumber;
const cc = res.countryCode;
const lineType = res.lineTypeIntelligence?.type ?? "unknown";
if (!ALLOW_COUNTRIES.has(cc)) {
throw Object.assign(new Error(`country_not_allowed: ${cc}`), { status: 422 });
}
if (DENY_LINE_TYPES.has(lineType)) {
throw Object.assign(new Error(`line_type_not_supported: ${lineType}`), { status: 422 });
}
log.info({ e164, cc, lineType }, "phone_normalized");
return { e164, countryCode: cc, lineType };
}
2) Pre-send gating for SMS with fallback to Voice
Decision logic:
- mobile/nonFixedVoip → SMS
- landline → Voice call
- voip → require Verify or alternate channel (policy)
Pseudo-implementation:
from twilio.rest import Client
import os
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
def route_notification(phone: str):
lookup = client.lookups.v2.phone_numbers(phone).fetch(fields="line_type_intelligence")
e164 = lookup.phone_number
lt = lookup.line_type_intelligence.get("type") if lookup.line_type_intelligence else "unknown"
if lt in ("mobile", "nonFixedVoip"):
msg = client.messages.create(
to=e164,
from_="+15005550006",
body="Your package is arriving today."
)
return {"channel": "sms", "sid": msg.sid}
if lt == "landline":
call = client.calls.create(
to=e164,
from_="+15005550006",
twiml="<Response><Say voice='Polly.Joanna'>Your package is arriving today.</Say></Response>"
)
return {"channel": "voice", "sid": call.sid}
raise ValueError(f"unsupported_line_type: {lt}")
3) Batch enrichment job with rate limiting and caching
Shell + curl + jq example (simple; production should use a proper worker):
set -euo pipefail
INPUT_CSV="phones.csv" # one phone per line
FIELDS="line_type_intelligence"
COUNTRY="US"
while read -r phone; do
# naive throttle: 5 req/s
sleep 0.2
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"https://lookups.twilio.com/v2/PhoneNumbers/${phone}?CountryCode=${COUNTRY}&Fields=${FIELDS}" \
| jq -c '{phone_number, country_code, line_type_intelligence}'
done < "$INPUT_CSV"
4) Studio Flow trigger with enriched params
Backend does Lookup, then triggers Studio:
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
-X POST "https://studio.twilio.com/v2/Flows/FW0123456789abcdef0123456789abcdef/Executions" \
--data-urlencode "To=+14155552671" \
--data-urlencode "From=+15005550006" \
--data-urlencode "Parameters={\"line_type\":\"mobile\",\"carrier\":\"T-Mobile USA, Inc.\"}"
Studio can branch on line_type to choose SMS vs Voice.
5) Verify enrollment: block VoIP for MFA
Flow:
- Lookup with
line_type_intelligence - If
voip/nonFixedVoip→ require TOTP or email - Else send Verify SMS
Python sketch:
import os
from twilio.rest import Client
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
def start_mfa(phone: str):
lookup = client.lookups.v2.phone_numbers(phone).fetch(fields="line_type_intelligence")
e164 = lookup.phone_number
lt = lookup.line_type_intelligence.get("type") if lookup.line_type_intelligence else "unknown"
if lt in ("voip", "nonFixedVoip"):
return {"action": "require_totp", "reason": f"line_type={lt}"}
verification = client.verify.v2.services("YOUR_VERIFY_SERVICE_SID") \
.verifications.create(to=e164, channel="sms")
return {"action": "verify_sms", "sid": verification.sid, "status": verification.status}
6) Support tool: on-demand caller name lookup
Only fetch caller_name when an agent opens a profile:
curl -sS -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"https://lookups.twilio.com/v2/PhoneNumbers/+14155552671?Fields=caller_name" | jq '.caller_name'
Quick Reference
| Task | Command / API | Key flags / params |
|---|---|---|
| Basic lookup | GET /v2/PhoneNumbers/{num} |
none |
| Lookup with line type + carrier | GET ...?Fields=line_type_intelligence |
Fields |
| Lookup with caller name | GET ...?Fields=caller_name |
Fields |
| National input parsing | .../{num}?CountryCode=US |
CountryCode |
| CLI fetch | twilio api:lookups:v2:phone-numbers:fetch |
--phone-number, --fields, --country-code, --profile |
| Node fetch | client.lookups.v2.phoneNumbers(num).fetch() |
{ fields, countryCode } |
| Python fetch | client.lookups.v2.phone_numbers(num).fetch() |
fields=... |
Graph Relationships
DEPENDS_ON
twilio-core-auth(Account SID/Auth Token or API Key auth patterns)twilio-cli(optional; for debugging and ops workflows)secrets-management(Vault/AWS/GCP secret injection)redis(optional; caching and rate limiting)
COMPOSES
twilio-messaging(pre-send gating, geo-matching Messaging Services, STOP handling, status webhooks)twilio-voice(fallback routing, TwiML generation, call recording/transcription)twilio-verify(E.164 normalization, VoIP restrictions, fraud guard)twilio-studio(Flow branching based on lookup enrichment)sendgrid-email(fallback channel when phone is invalid/unreachable)
SIMILAR_TO
libphonenumber(local parsing/formatting; not authoritative for reachability/carrier)numverify/ other phone intelligence providers (feature overlap; different coverage/cost)HLR lookupservices (carrier/reachability in some regions; different semantics and legality constraints)