skills/twilio/twilio-admin/SKILL.md
Admin: sub-account lifecycle, usage monitoring, number management, compliance SHAKEN/STIR TCR, audit logs
npx skillsauth add alphaonedev/openclaw-graph twilio-adminInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Enable reliable, auditable administration of Twilio accounts in production:
This skill is for engineers who need deterministic, scriptable control over Twilio admin surfaces without breaking production traffic.
ACCOUNT_SID (starts with AC...)AUTH_TOKENAC...) and (optionally) its auth token if using per-subaccount credentials.5.6.020.11.1 (LTS)3.11.89.4.11.78.5.03.0.13 (for TLS inspection / cert tooling)25.0.322.04 / 24.0439 / 4014 (Sonoma) on Intel + Apple SiliconNote: Twilio’s classic model uses Account SID + Auth Token. Some products support API Keys; for admin operations, you often still need Account SID + Auth Token. Treat Auth Token as a root secret.
Operational model:
Twilio accounts have a status field:
active: normal operationsuspended: traffic blocked; resources retainedclosed: account closed; resources may be released; irreversible in practiceTwilio numbers are resources with:
sms, mms, voice, faxAt scale:
Repository: https://github.com/twilio/twilio-python
PyPI: pip install twilio · Supported: Python 3.7–3.13
from twilio.rest import Client
client = Client()
# List subaccounts
for acct in client.api.v2010.accounts.list():
print(acct.sid, acct.friendly_name, acct.status)
# Create subaccount
sub = client.api.v2010.accounts.create(friendly_name="Staging Account")
sub_client = Client(client.username, client.password, sub.sid)
# Rotate auth token (master account key management)
keys = client.api.v2010.accounts(client.account_sid).keys.list()
for k in keys:
print(k.sid, k.friendly_name, k.date_created)
Source: twilio/twilio-python — accounts
sudo apt-get update
sudo apt-get install -y curl jq python3.11 python3.11-venv python3-pip ca-certificates gnupg
Install Node.js 20.11.1 (NodeSource):
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version # expect v20.x
npm --version
Install Twilio CLI 5.6.0:
sudo npm install -g [email protected]
twilio --version
Optional: install Twilio CLI plugins commonly used in admin workflows:
twilio plugins:install @twilio-labs/[email protected]
twilio plugins:install @twilio-labs/[email protected]
twilio plugins:install @twilio-labs/[email protected]
Python environment:
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip==24.0
pip install twilio==9.4.1 requests==2.31.0 python-dateutil==2.9.0.post0
sudo dnf install -y curl jq python3.11 python3.11-pip python3.11-virtualenv ca-certificates
sudo dnf install -y nodejs npm
node --version
Install Twilio CLI:
sudo npm install -g [email protected]
twilio --version
Python venv:
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip==24.0
pip install twilio==9.4.1 requests==2.31.0 python-dateutil==2.9.0.post0
Install Homebrew (if needed), then:
brew update
brew install jq [email protected] node@20
Ensure Node 20 is active:
brew link --overwrite node@20
node --version
Install Twilio CLI:
npm install -g [email protected]
twilio --version
Python venv:
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip==24.0
pip install twilio==9.4.1 requests==2.31.0 python-dateutil==2.9.0.post0
Twilio CLI stores profiles under:
~/.twilio-cli/config.jsonLogin interactively (writes profile):
twilio login
Non-interactive (CI) via env vars:
export TWILIO_ACCOUNT_SID="ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export TWILIO_AUTH_TOKEN="your_auth_token"
Verify:
twilio api:core:accounts:fetch --sid "$TWILIO_ACCOUNT_SID"
Operations:
Key production guardrails:
Notes:
- Twilio CLI command groups vary by installed plugins and CLI version.
- When CLI coverage is incomplete, use direct REST API calls with
curlortwilio-python.
Common flags (apply to most commands):
-o, --output [json|tsv|csv|human]: output format--properties <props>: select fields (comma-separated)--no-header: suppress header (tsv/csv)--limit <n>: limit list results--page-size <n>: page size for list--page-token <token>: pagination token--profile <name>: use a named profile from ~/.twilio-cli/config.json--log-level [debug|info|warn|error]: CLI logging verbosityExamples:
twilio api:core:accounts:list -o json --limit 50
twilio api:core:incoming-phone-numbers:list --properties sid,phoneNumber,friendlyName -o tsv --no-header
List accounts:
twilio api:core:accounts:list --limit 200 -o json
Fetch account:
twilio api:core:accounts:fetch --sid ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -o json
Create subaccount:
twilio api:core:accounts:create \
--friendly-name "prod-tenant-acme" \
-o json
Update account status (suspend/reactivate):
twilio api:core:accounts:update --sid ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --status suspended -o json
twilio api:core:accounts:update --sid ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --status active -o json
Close account:
twilio api:core:accounts:update --sid ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --status closed -o json
Production rule: closing is effectively irreversible. Prefer
suspendedfirst, then close after cleanup.
List usage records (daily):
twilio api:core:usage:records:list \
--category sms \
--start-date 2026-02-01 \
--end-date 2026-02-20 \
--granularity daily \
-o json
Flags (Usage Records list):
--category <string>: e.g. sms, mms, calls, phonenumbers, verify, etc.--start-date YYYY-MM-DD--end-date YYYY-MM-DD--granularity [daily|hourly|all]Category names are Twilio-defined; enumerate by inspecting existing usage or Twilio docs for your account.
List numbers:
twilio api:core:incoming-phone-numbers:list --limit 200 -o json
Fetch number by SID:
twilio api:core:incoming-phone-numbers:fetch --sid PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -o json
Update number configuration:
twilio api:core:incoming-phone-numbers:update \
--sid PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
--friendly-name "prod-sms-us-east-01" \
--sms-url "https://api.example.com/twilio/sms/inbound" \
--sms-method POST \
--status-callback "https://api.example.com/twilio/sms/status" \
--status-callback-method POST \
--voice-url "https://api.example.com/twilio/voice/inbound" \
--voice-method POST \
-o json
Release number:
twilio api:core:incoming-phone-numbers:remove --sid PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Search US local numbers with SMS+Voice:
twilio api:core:available-phone-numbers:local:list \
--iso-country US \
--in-region CA \
--sms-enabled true \
--voice-enabled true \
--limit 20 \
-o json
Buy a number:
twilio api:core:incoming-phone-numbers:create \
--phone-number "+14155552671" \
--friendly-name "prod-acme-us-ca-01" \
-o json
List messaging services (requires messaging API group; CLI coverage may vary):
If CLI supports it:
twilio api:messaging:v1:services:list -o json --limit 200
Create service:
twilio api:messaging:v1:services:create \
--friendly-name "prod-us-messaging" \
--use-inbound-webhook-on-number true \
--sticky-sender true \
--smart-encoding true \
-o json
Add a phone number to a service (phone number SID):
twilio api:messaging:v1:services:phone-numbers:create \
--service-sid MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
--phone-number-sid PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
-o json
Enable geo-match (where supported by service settings; may require API fields not exposed in CLI): Use REST API via curl if needed.
CLI support is inconsistent; use REST API with curl or twilio-python.
Base auth header:
export TWILIO_ACCOUNT_SID="ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
export TWILIO_AUTH_TOKEN="your_auth_token"
basic_auth="$(printf '%s:%s' "$TWILIO_ACCOUNT_SID" "$TWILIO_AUTH_TOKEN" | base64)"
Example: list TrustHub customer profiles (endpoint may vary by region/product availability):
curl -sS "https://trusthub.twilio.com/v1/CustomerProfiles" \
-H "Authorization: Basic $basic_auth" | jq .
If you receive 404, your account may not have TrustHub enabled or the endpoint differs for your product set. Confirm in Console and Twilio docs for your account.
Twilio’s audit event API availability varies. If your account supports it, you may have endpoints under https://accounts.twilio.com/v1/... or similar. In many production setups, you should treat Twilio Console audit logs as a human-accessible source and implement your own automation audit logs.
Path:
~/.twilio-cli/config.jsonExample (multiple profiles):
{
"profiles": {
"prod-parent": {
"accountSid": "YOUR_ACCOUNT_SID",
"authToken": "prod_parent_auth_token",
"region": "us1",
"edge": "ashburn"
},
"stage-parent": {
"accountSid": "YOUR_ACCOUNT_SID",
"authToken": "stage_parent_auth_token",
"region": "us1"
}
},
"activeProfile": "prod-parent"
}
Operational guidance:
chmod 600 ~/.twilio-cli/config.json
Path (repo-local):
./.openclaw/skills/twilio-admin.tomlExample:
[twilio]
parent_account_sid = "YOUR_ACCOUNT_SID"
region = "us1"
edge = "ashburn"
[policy]
require_ticket_id = true
allowed_close_environments = ["dev", "sandbox"]
suspend_before_close = true
suspend_grace_period_hours = 24
[usage_alerts]
currency = "USD"
daily_spend_soft_limit = 250.00
daily_spend_hard_limit = 500.00
anomaly_multiplier = 2.5
lookback_days = 7
[number_management]
default_sms_url = "https://api.example.com/twilio/sms/inbound"
default_status_callback = "https://api.example.com/twilio/sms/status"
default_voice_url = "https://api.example.com/twilio/voice/inbound"
Path (CI system):
Required:
export TWILIO_ACCOUNT_SID="YOUR_ACCOUNT_SID"
export TWILIO_AUTH_TOKEN="prod_parent_auth_token"
Optional (recommended for audit trail):
export CHANGE_TICKET_ID="INC-20481"
export CHANGE_ACTOR="[email protected]"
export CHANGE_REASON="Suspend tenant due to suspected SMS fraud"
Pattern:
Example pipeline (pseudo CI job):
python scripts/twilio_usage_anomaly.py --since 2026-02-20 --threshold-multiplier 3.0
python scripts/twilio_suspend_subaccount.py --subaccount AC333... --ticket INC-20481 --reason "SMS spike"
python scripts/post_to_pagerduty.py --incident-key twilio-ac333 --summary "Suspended Twilio subaccount AC333..."
Twilio Terraform provider coverage is partial and changes over time. Use Terraform for stable resources (where supported) and use this skill for:
Pattern:
Export usage records daily into BigQuery/Snowflake:
usage_records table keyed by account_sid, category, date, price, usage.Then join with:
30003)Include both CLI and API-level errors. For each: symptom, root cause, fix.
Error:
Twilio could not authenticate the request. Please check your credentials. (20003)
Root causes:
TWILIO_AUTH_TOKENFix:
twilio profiles:list
twilio api:core:accounts:fetch --sid "$TWILIO_ACCOUNT_SID"
Error:
The 'To' number +1415555 is not a valid phone number. (21211)
Root causes:
Fix:
Error:
Rate limit exceeded (20429)
Root causes:
Fix:
for sid in $(cat subaccounts.txt); do
twilio api:core:accounts:fetch --sid "$sid" -o json
sleep 0.3
done
Error (messaging status callback / logs):
Message Delivery - Error Code 30003: Unreachable destination handset
Root causes:
Fix:
Error:
The requested resource /IncomingPhoneNumbers/PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX was not found (20404)
Root causes:
Fix:
Symptom:
Root causes:
Fix:
Symptom:
Root causes:
Fix:
MessageSid / CallSid.Symptom (Twilio debugger / logs):
Root causes:
Fix:
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts
Symptom:
Root causes:
Fix:
Symptom:
Root causes:
Fix:
TWILIO_AUTH_TOKEN only in a secrets manager.Local filesystem:
~/.twilio-cli/config.json must be 0600.CHANGE_TICKET_ID for destructive actions (suspend/close/release numbers).chmod 600)Before:
After:
Expected impact:
20429 Rate limit exceeded.Python pattern (requests session + bounded concurrency):
import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from twilio.rest import Client
client = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"])
def fetch_usage(acct_sid: str):
sub = Client(os.environ["TWILIO_ACCOUNT_SID"], os.environ["TWILIO_AUTH_TOKEN"], account_sid=acct_sid)
recs = sub.usage.records.list(category="sms", granularity="daily", limit=30)
return acct_sid, sum(float(r.price or 0) for r in recs)
accounts = client.api.accounts.list(limit=200)
with ThreadPoolExecutor(max_workers=8) as ex:
futs = [ex.submit(fetch_usage, a.sid) for a in accounts]
for f in as_completed(futs):
sid, total = f.result()
print(sid, total)
time.sleep(0.05)
Expected impact:
Expected impact:
status=suspended).Before releasing a number:
Twilio supports regions/edges; CLI profile can set region and edge.
If your org uses SendGrid:
Create subaccount:
twilio api:core:accounts:create --friendly-name "prod-tenant-acme" -o json | jq -r '.sid'
Store mapping (example local file):
./inventory/twilio_subaccounts.jsonmkdir -p inventory
twilio api:core:accounts:list -o json --limit 200 > inventory/twilio_subaccounts.json
Buy a number and configure webhooks:
twilio api:core:incoming-phone-numbers:create \
--phone-number "+14155552671" \
--friendly-name "prod-acme-primary" \
--sms-url "https://api.example.com/twilio/sms/inbound" \
--sms-method POST \
--status-callback "https://api.example.com/twilio/sms/status" \
--status-callback-method POST \
--voice-url "https://api.example.com/twilio/voice/inbound" \
--voice-method POST \
-o json
Suspend:
export CHANGE_TICKET_ID="INC-20481"
export CHANGE_REASON="Suspected SMS pumping; usage spike 4x baseline"
twilio api:core:accounts:update --sid YOUR_ACCOUNT_SID --status suspended -o json
Verify status:
twilio api:core:accounts:fetch --sid YOUR_ACCOUNT_SID -o json | jq -r '.status'
List subaccounts:
twilio api:core:accounts:list -o json --limit 200 > /tmp/accounts.json
Extract SIDs:
jq -r '.[].sid' /tmp/accounts.json > /tmp/account_sids.txt
Loop usage (daily SMS) and emit CSV:
echo "account_sid,total_price_usd" > /tmp/sms_usage.csv
while read -r sid; do
total="$(twilio api:core:usage:records:list --category sms --start-date 2026-02-20 --end-date 2026-02-20 --granularity daily -o json \
| jq '[.[].price | tonumber] | add // 0')"
echo "$sid,$total" >> /tmp/sms_usage.csv
sleep 0.2
done < /tmp/account_sids.txt
Create service:
twilio api:messaging:v1:services:create \
--friendly-name "prod-us-messaging" \
--sticky-sender true \
--smart-encoding true \
-o json | jq -r '.sid'
Add numbers:
export MG_SID="YOUR_MG_SID"
for pn in PNaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa PNbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; do
twilio api:messaging:v1:services:phone-numbers:create \
--service-sid "$MG_SID" \
--phone-number-sid "$pn" \
-o json
done
List numbers with friendly name filter (client-side):
twilio api:core:incoming-phone-numbers:list -o json --limit 200 \
| jq -r '.[] | select(.friendlyName | test("deprecated")) | .sid + " " + .phoneNumber'
Remove:
twilio api:core:incoming-phone-numbers:remove --sid PNXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TLS check:
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -issuer -subject
HTTP health:
curl -sS -o /dev/null -w "%{http_code}\n" https://api.example.com/twilio/sms/status/health
| Task | Command | Key flags |
|---|---|---|
| List subaccounts | twilio api:core:accounts:list | --limit, -o json |
| Create subaccount | twilio api:core:accounts:create | --friendly-name |
| Suspend/reactivate | twilio api:core:accounts:update | --sid, --status suspended|active |
| Close account | twilio api:core:accounts:update | --status closed |
| Daily usage | twilio api:core:usage:records:list | --category, --start-date, --end-date, --granularity |
| List numbers | twilio api:core:incoming-phone-numbers:list | --limit, --properties |
| Buy number | twilio api:core:incoming-phone-numbers:create | --phone-number, --friendly-name |
| Update number webhooks | twilio api:core:incoming-phone-numbers:update | --sms-url, --status-callback, --voice-url |
| Release number | twilio api:core:incoming-phone-numbers:remove | --sid |
| Search available numbers | twilio api:core:available-phone-numbers:local:list | --iso-country, --in-region, --sms-enabled, --voice-enabled |
[email protected][email protected][email protected][email protected][email protected]twilio-messaging (status callbacks, STOP handling, 10DLC, toll-free verification)twilio-voice (TwiML apps, SIP trunking, recordings/transcriptions, IVR state machines)twilio-verify (Verify V2, Fraud Guard, rate limiting)sendgrid-admin (templates, inbound parse, IP warming, bounce/spam handling)studio-admin (Flow export/import, REST Trigger API, A/B testing)aws-organizations-admin (account lifecycle + guardrails)gcp-resource-manager-adminstripe-admin (billing/usage monitoring patterns)okta-admin (audit/change control patterns)tools
Root web development: project structure, tooling selection, deployment decisions
development
WebAssembly: Rust/Go/C to WASM, wasm-bindgen, Emscripten, WASM Component Model
development
Vue 3: Composition API script setup, Pinia, Vue Router 4, SFCs, Vite, Nuxt 3
tools
Tailwind CSS 4: utility classes, config, JIT, arbitrary values, darkMode, plugins, shadcn/ui