Skip to content
Gauge

ax-check.com

Twilio

twilio.com

ax-check.com

Checked 29 September 2026

B

81/100

Agents need your login

Twilio docs and quickstart are agent-ready; live pricing page wasn't checked

17 of 23 checklist items pass, including a working quickstart with real commands and code. Pricing page itself was never fetched, so 3 items there are unassessed.

2 fixes to start with

Concrete changes, backed by what we found.

Onboarding

Give agents a clear login handoff and a way to resume setup.

Agents cannot progress through onboarding without human help. Provide an agent-accessible path through authentication and setup.

Docs

Return Markdown when clients request text/markdown.

Homepage returned text/html for a Markdown Accept header; no Markdown representation served.

Recorded coding sessions

Same task, 3 models, no twilio.com account. Recorded 29 September 2026 on Pi.

3 recorded · 2m 6s

2m 6s · recorded

Session findings

Select a finding to see it in context.

piv0.84.2
modelQwen 3.8 Maxcwd/sandbox/repo
Help me build a simple example using Twilio. Tell me how pricing works, and briefly tell me whether this product will be easy for you to manage. Let me know if you get blocked. If this product has no developer workflow you can act on, say so plainly and stop. Stay light: use the hosted product through its SDK or API. Do not start local service stacks or wait for long-running commands; if the quickstart requires either, say so plainly and stop.
Thinking…

The user wants a simple Twilio example. Let me check the repo first, and check if there's a Twilio account available. There's no credentials presumably. Let me look at the environment.

Let me check the sandbox repo state.

I'll start by looking at the current repo and environment.

$ pwd; ls -la; echo "---"; ls -a /sandbox 2>/dev/null{"content":[{"text":"/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 29 08:16 ..\ndrwxr-xr-x 8 root root 40…ctrl+o
{"content":[{"text":"/sandbox/repo\ntotal 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 29 08:16 ..\ndrwxr-xr-x 8 root root 4096 Sep 24 20:05 .git\n-rw-r--r-- 1 root root  408 Sep 18 08:00 .gitignore\n---\n.\n..\n.cache\n.gauge\n.pi\n.tmp\nrepo\n","type":"text"}]}
$ env | grep -iE 'twilio|account_sid|auth_token|api_key' | sed 's/=.*/=<set>/'; echo "---"; which node python3 curl pip 2>/dev/null; node --version 2>/dev/null; python3 --version 2>/dev/null{"content":[{"text":"PI_GATEWAY_API_KEY=<set>\n---\n/usr/local/bin/node\n/usr/local/bin/python3\n/usr/bin/curl\n/usr/local/bin/pip\nv22.23.2\nPython 3.12.3\n","…ctrl+o
{"content":[{"text":"PI_GATEWAY_API_KEY=<set>\n---\n/usr/local/bin/node\n/usr/local/bin/python3\n/usr/bin/curl\n/usr/local/bin/pip\nv22.23.2\nPython 3.12.3\n","type":"text"}]}
Thinking…

No Twilio credentials in env. Let me check network access and whether pip/npm installs work.

Let me check network connectivity to Twilio API.

No Twilio credentials present. Let me check network access and SDK availability.

$ cd /tmp && curl -s -m 12 -o /dev/null -w "pypi:%{http_code}\n" https://pypi.org/simple/twilio/ ; curl -s -m 12 -o /dev/null -w "npm:%{http_code}\n" https://registry.npmjs.org/twilio ; curl -s -m 12 -o…{"content":[{"text":"pypi:200\nnpm:200\ntwilio_api:401\n","type":"text"}]}ctrl+o
cd /tmp && curl -s -m 12 -o /dev/null -w "pypi:%{http_code}\n" https://pypi.org/simple/twilio/ ; curl -s -m 12 -o /dev/null -w "npm:%{http_code}\n" https://registry.npmjs.org/twilio ; curl -s -m 12 -o /dev/null -w "twilio_api:%{http_code}\n" https://api.twilio.com/2010-04-01/Accounts.json

$ cat /sandbox/repo/.gitignore; echo "--- git log"; cd /sandbox/repo && git log --oneline -5 2>&1 | head{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/…ctrl+o
{"content":[{"text":"# Keep generated and vendored files out of source diffs.\n\n# Logs / OS cruft\n*.log\n.DS_Store\n\n# JavaScript / TypeScript\nnode_modules/\n.next/\ndist/\nbuild/\ncoverage/\n\n# Python\n.venv/\nvenv/\n__pycache__/\n*.py[cod]\n.pytest_cache/\n*.egg-info/\n\n# Rust\ntarget/\n\n# Go / PHP / Ruby vendored deps\nvendor/\n.bundle/\n\n# Java / Kotlin / Scala\n.gradle/\n\n# .NET\nbin/\nobj/\n\n# Elixir\n_build/\ndeps/\n\n# Dart / Flutter\n.dart_tool/\n--- git log\n9ebf5fb Initial workspace\n","type":"text"}]}
Thinking…

Network works. api.twilio.com returns 401 (unauthenticated) — good, that means it's reachable and requires auth.

No credentials. So I can't actually send a message. But I can build the example code that uses the SDK/API. Let me install the SDK and verify the example code runs up to the auth boundary (i.e., it produces a clean auth error, proving the code path works).

Let me install twilio python SDK.

Actually, let me decide on language. Python is simple. Let me use the twilio Python helper library — it's the official SDK and it's a hosted product (no local stacks needed). Sending an SMS is a single REST API call.

Let me install.

Network works and api.twilio.com is reachable (401 = needs auth). Let me install the official SDK.

$ cd /sandbox/repo && pip install --quiet twilio 2>&1 | tail -5; python3 -c "import twilio; print('twilio SDK', twilio.__version__)"{"content":[{"text":"twilio SDK 9.11.2\n","type":"text"}]}
Thinking…

SDK installed. Now let me write a simple example. I'll create a small project.

Let me build:

  • send_sms.py — sends an SMS via Twilio API
  • .env.example — credentials template
  • README with pricing notes

Let me write the code and then verify it runs to the auth boundary (proving the code path is correct) without credentials.

Let me write the files.

SDK installed. Now let me build the example.

write /sandbox/repo/twilio_sms/.env.example{"content":[{"text":"Successfully wrote 365 bytes to /sandbox/repo/twilio_sms/.env.example","type":"text"}]}
write /sandbox/repo/twilio_sms/send_sms.py{"content":[{"text":"Successfully wrote 1742 bytes to /sandbox/repo/twilio_sms/send_sms.py","type":"text"}]}
Thinking…

Now let me verify the code path works. I have no real credentials, so I'll run it with fake credentials to confirm the request actually reaches Twilio's API and comes back with a proper auth error (20003). That proves the wiring is correct without sending anything or spending money.

Now let me verify the wiring reaches Twilio's real API. I'll use deliberately fake credentials so nothing is sent or billed.

$ cd /sandbox/repo/twilio_sms && TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000 TWILIO_AUTH_TOKEN=fake_token TWILIO_FROM_NUMBER=+15551234567 TWILIO_TO_NUMBER=+15559876543 python3 send_sms.py "wir…{"content":[{"text":"Twilio rejected the request (20003): Unable to create record: auth account AC00000000000000000000000000000000 does not exist\nexit=1\n","ty…ctrl+o
cd /sandbox/repo/twilio_sms && TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000 TWILIO_AUTH_TOKEN=fake_token TWILIO_FROM_NUMBER=+15551234567 TWILIO_TO_NUMBER=+15559876543 python3 send_sms.py "wiring test"; echo "exit=$?"

{"content":[{"text":"Twilio rejected the request (20003): Unable to create record: auth account AC00000000000000000000000000000000 does not exist\nexit=1\n","type":"text"}]}
Thinking…

The wiring works. Error 20003 = authentication error, which is exactly what we expect with fake credentials. The request reached Twilio's real API, was properly formatted, signed, and rejected only because the account doesn't exist. So the code path is verified correct.

Also test the missing-env-var guard.

Now let me add a README with pricing info. Let me be accurate about Twilio pricing. I should be careful — I know Twilio pricing generally but exact numbers change. Let me state it accurately but hedge on exact current numbers, or better, note where to check. Actually let me give approximate figures I'm reasonably confident about:

  • SMS/MMS: US outbound SMS ~$0.0079 per message segment (this has been the standard rate for a while). Inbound ~$0.0079.
  • Phone numbers: US local number ~$1.15/month.
  • Trial accounts: ~$15 trial credit, limited to verified numbers, message prefixed with "Sent from your Twilio trial account".
  • Carrier fees: US A2P 10DLC registration/brand+campaign fees, and per-message carrier fees were added (~$0.003/msg carrier fees for US).
  • Pricing varies by country, and is per-segment (long messages split into 160-char GSM-7 segments).
  • Billing is pay-as-you-go, no minimum, prepaid.

I should flag that exact rates change and point to twilio.com/en-us/pricing. Let me be honest that I'm giving approximate numbers as of my knowledge and the authoritative source is the pricing page.

Let me check if I can fetch the pricing page to get accurate numbers. Let me try.

Actually, let me try fetching the pricing page to be accurate.

Wiring confirmed — the request reached Twilio's real API and was rejected only for auth (code 20003), exactly as expected with fake credentials. Let me check the env-var guard and try to fetch current pricing.

$ cd /sandbox/repo/twilio_sms && env -u TWILIO_ACCOUNT_SID -u TWILIO_AUTH_TOKEN python3 send_sms.py; echo "exit=$?"{"content":[{"text":"Missing TWILIO_ACCOUNT_SID. Copy .env.example to .env, fill it in, then run:\n set -a; source .env; set +a\n python3 send_sms.py\nexi…ctrl+o
{"content":[{"text":"Missing TWILIO_ACCOUNT_SID. Copy .env.example to .env, fill it in, then run:\n    set -a; source .env; set +a\n    python3 send_sms.py\nexit=1\n","type":"text"}]}
$ cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" https://www.twilio.com/en-us/pricing -o p.html -w "http:%{http_code} bytes:%{size_download}\n"; grep -oiE '\$0\.00[0-9]{1,3}' p.html | sort | uniq -c | sort …{"content":[{"text":"http:200 bytes:682030\n 3 $0.005\n 2 $0.0083\n 2 $0.007\n 2 $0.0001\n 1 $0.0085\n 1 $0.0045\n 1 $0.0028\…ctrl+o
cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" https://www.twilio.com/en-us/pricing -o p.html -w "http:%{http_code} bytes:%{size_download}\n"; grep -oiE '\$0\.00[0-9]{1,3}' p.html | sort | uniq -c | sort -rn | head -20

{"content":[{"text":"http:200 bytes:682030\n      3 $0.005\n      2 $0.0083\n      2 $0.007\n      2 $0.0001\n      1 $0.0085\n      1 $0.0045\n      1 $0.0028\n      1 $0.0025\n      1 $0.002\n      1 $0.0013\n      1 $0.0002\n","type":"text"}]}
Thinking…

The guard works. Now let me extract more accurate pricing info from the page. Let me look at context around the SMS pricing.

Let me grep for "per message" and "SMS" contexts.

Guard works. Let me extract the actual pricing context from the fetched page.

$ cd /tmp && python3 - <<'EOF'{"content":[{"text":"[phone number] ...n of a right chevron SMS WhatsApp RCS Voice An icon of a right chevron SIP Trunking Email An icon of a right chevron SMTP…ctrl+o
cd /tmp && python3 - <<'EOF'
import re,html
t=open('p.html',encoding='utf-8',errors='ignore').read()
t=re.sub(r'<script.*?</script>','',t,flags=re.S)
t=re.sub(r'<style.*?</style>','',t,flags=re.S)
t=html.unescape(re.sub(r'<[^>]+>',' ',t))
t=re.sub(r'\s+',' ',t)
for kw in ['Outbound SMS','Inbound SMS','phone number','monthly','carrier fees','10DLC','per message segment']:
    for m in re.finditer(kw,t,re.I):
        print(f"[{kw}] ...{t[max(0,m.start()-130):m.start()+150]}...\n")
        break
EOF

{"content":[{"text":"[phone number] ...n of a right chevron SMS WhatsApp RCS Voice An icon of a right chevron SIP Trunking Email An icon of a right chevron SMTP Service Phone Numbers An icon of a right chevron Toll-free 10DLC Short Codes Video API An icon of a right chevron Flex An icon of a right chevron Communicatio...\n\n[monthly] ... \"> Support plans Basic support to personalized plans for your Twilio account. Developer plans are free, all other plans based on monthly spend. View support plans Contact sales The Twilio advantage Communicate reliably Experience a 99.95% uptime SLA made possible with automated ...\n\n[10DLC] ... of a right chevron SIP Trunking Email An icon of a right chevron SMTP Service Phone Numbers An icon of a right chevron Toll-free 10DLC Short Codes Video API An icon of a right chevron Flex An icon of a right chevron Communications Authentication An icon of a right chevron An ico...\n\n","type":"text"}]}
Thinking…

The pricing page is a JS-driven navigation page; the actual per-message numbers are loaded dynamically or on sub-pages. Let me try the SMS-specific pricing page.

Let me try https://www.twilio.com/en-us/pricing/sms or the messaging pricing page.

Actually, I found $0.0079 wasn't in the list. Let me search for the SMS sub-page.

Let me be efficient. I'll try one more fetch for the SMS pricing detail, and if it's JS-rendered, I'll just state pricing structurally and point to the authoritative page rather than inventing numbers.

Let me try.

The main page is JS-rendered nav. Let me try the SMS-specific pricing page.

$ cd /tmp && for u in https://www.twilio.com/en-us/pricing/sms https://www.twilio.com/en-us/sms/pricing; do curl -sL -m 20 -A "Mozilla/5.0" "$u" -o s.html -w "$u http:%{http_code} bytes:%{size_download}…{"content":[{"text":"https://www.twilio.com/en-us/pricing/sms http:404 bytes:612554\n [Outbound] ...ge An icon of a right chevron Audiences Journeys Customer D…ctrl+o
cd /tmp && for u in https://www.twilio.com/en-us/pricing/sms https://www.twilio.com/en-us/sms/pricing; do curl -sL -m 20 -A "Mozilla/5.0" "$u" -o s.html -w "$u http:%{http_code} bytes:%{size_download}\n"; python3 - <<'EOF'
import re,html
t=open('s.html',encoding='utf-8',errors='ignore').read()
t=re.sub(r'<(script|style).*?</\1>','',t,flags=re.S)
t=html.unescape(re.sub(r'<[^>]+>',' ',t)); t=re.sub(r'\s+',' ',t)
for kw in ['Outbound','Inbound','per message','segment','local phone number']:
    m=re.search(kw,t,re.I)
    if m: print(f"  [{kw}] ...{t[max(0,m.start()-100):m.start()+130]}...")
EOF
done

{"content":[{"text":"https://www.twilio.com/en-us/pricing/sms http:404 bytes:612554\n  [Outbound] ...ge An icon of a right chevron Audiences Journeys Customer Data See all products <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.9820...\n  [segment] ...g in to Twilio Console Conversations, Communications and Authentication Or log in to access: Twilio Segment Customer Data Platform Twilio SendGrid Email and Marketing Campaigns Twilio logo Products An icon of a down chevron Twilio...\nhttps://www.twilio.com/en-us/sms/pricing http:200 bytes:824270\n  [Outbound] ...ge An icon of a right chevron Audiences Journeys Customer Data See all products <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.9820...\n  [Inbound] ...control over your messaging costs, with discounts as you scale. Phone number type SMS Outbound* SMS Inbound* MMS Outbound* MMS Inbound* Long codes A 10-digit number for SMS and voice calls with localized identity. $0.0083 $0.0083 ...\n  [per message] ...S A2P 10DLC are subject to registration onboarding fees . A failed message processing fee of $0.001 per message will be applied only to messages that terminate in a \"Failed\" status. <title>An icon of a outbound link arrow</title> ...\n  [segment] ...g in to Twilio Console Conversations, Communications and Authentication Or log in to access: Twilio Segment Customer Data Platform Twilio SendGrid Email and Marketing Campaigns Twilio logo Products An icon of a down chevron Twilio...\n","type":"text"}]}
Thinking…

I got real numbers from the SMS pricing page:

  • Long codes: SMS Outbound $0.0083, SMS Inbound $0.0083
  • Failed message processing fee: $0.001 per message

Let me extract more detail: the phone number types table, and phone number monthly costs, and carrier fees / 10DLC.

Let me extract the full table region.

Got real numbers. Let me extract the full US pricing table.

$ cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" "https://www.twilio.com/en-us/sms/pricing" -o s.html && python3 - <<'EOF'{"content":[{"text":"=== US RATE TABLE ===\nPhone number type SMS Outbound* SMS Inbound* MMS Outbound* MMS Inbound* Long codes A 10-digit number for SMS and voi…ctrl+o
cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" "https://www.twilio.com/en-us/sms/pricing" -o s.html && python3 - <<'EOF'
import re,html
t=open('s.html',encoding='utf-8',errors='ignore').read()
t=re.sub(r'<(script|style).*?</\1>','',t,flags=re.S)
t=html.unescape(re.sub(r'<[^>]+>',' ',t)); t=re.sub(r'\s+',' ',t)
i=t.find('Phone number type SMS Outbound')
print("=== US RATE TABLE ===")
print(t[i:i+1400])
print("\n=== CARRIER / 10DLC / NUMBER FEES ===")
for kw in ['carrier fees','registration onboarding fees','per month','Toll-free','Short codes']:
    for m in re.finditer(kw,t,re.I):
        print(f"\n[{kw}] ...{t[max(0,m.start()-260):m.start()+260]}...")
        break
EOF

{"content":[{"text":"=== US RATE TABLE ===\nPhone number type SMS Outbound* SMS Inbound* MMS Outbound* MMS Inbound* Long codes A 10-digit number for SMS and voice calls with localized identity. $0.0083 $0.0083 $0.022 $0.0165 Toll-free Business messaging phone number with a 3-digit prefix (Ex: 887) for high-throughput SMS and voice calls. $0.0083 $0.0083 $0.022 $0.02 Short codes A short number (5-6 digits) designed for high-throughput two-way SMS messaging. $0.0083 $0.0083 $0.022 $0.0165 <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.98207L35.951 3.59351\" stroke=\"#F22F46\" stroke-width=\"5.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/> </svg> \"> * Text messages are charged per segment. Learn more about character limits and message segments . Prices may change from time to time without notice and additional carrier fees may apply. See the carrier fees section below for more detail. US A2P 10DLC are subject to registration onboarding fees . A failed message processing fee of $0.001 per message will be applied only to messages that terminate in a \"Failed\" status. <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.98207L35.951 3.59351\" stroke=\"#F22F46\" stroke-width=\"5.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/> </svg> \"> Sta\n\n=== CARRIER / 10DLC / NUMBER FEES ===\n\n[carrier fees] ...path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.98207L35.951 3.59351\" stroke=\"#F22F46\" stroke-width=\"5.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/> </svg> \"> Jump to: SMS/MMS RCS pricing Volume discounts Carrier fees Features Phone number pricing United States SMS/MMS Pricing <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.98207L35.951 3.59351\" stroke=\"#F22F46\" st...\n\n[registration onboarding fees] ...essages are charged per segment. Learn more about character limits and message segments . Prices may change from time to time without notice and additional carrier fees may apply. See the carrier fees section below for more detail. US A2P 10DLC are subject to registration onboarding fees . A failed message processing fee of $0.001 per message will be applied only to messages that terminate in a \"Failed\" status. <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M7...\n\n[per month] ...ke-linejoin=\"round\"/> </svg> \"> Choose from a deep inventory of phone numbers to select the right number type for your use case. Phone number charges are added to your message volume and carrier costs for your total price. Phone number type Twilio leased Cost per month Bring your own number Cost per month Long codes A 10-digit number for SMS and voice calls with localized identity. $1.15 $0.5 Toll-free Business messaging phone number with a 3-digit prefix (Ex: 887) for high-throughput SMS and voice calls. $2.15 $0....\n\n[Toll-free] ...on of a right chevron An icon of a plus symbol An icon of a minus symbol Messaging An icon of a right chevron SMS WhatsApp RCS Voice An icon of a right chevron SIP Trunking Email An icon of a right chevron SMTP Service Phone Numbers An icon of a right chevron Toll-free 10DLC Short Codes Video API An icon of a right chevron Flex An icon of a right chevron Communications Authentication An icon of a right chevron An icon of a plus symbol An icon of a minus symbol Verify An icon of a right chevron Lookup An icon of a r...\n\n[Short codes] ...evron An icon of a plus symbol An icon of a minus symbol Messaging An icon of a right chevron SMS WhatsApp RCS Voice An icon of a right chevron SIP Trunking Email An icon of a right chevron SMTP Service Phone Numbers An icon of a right chevron Toll-free 10DLC Short Codes Video API An icon of a right chevron Flex An icon of a right chevron Communications Authentication An icon of a right chevron An icon of a plus symbol An icon of a minus symbol Verify An icon of a right chevron Lookup An icon of a right chevron Aut...\n","type":"text"}]}
Thinking…

I have solid, current numbers scraped directly from Twilio's official pricing page. Let me also get the carrier fees detail. Let me look for the carrier fees section content.

Let me search for specific carrier fee numbers.

Actually, I have enough. Let me grab the carrier fees table quickly and then write the README.

I have authoritative current numbers. Let me grab the carrier-fees detail.

$ cd /tmp && python3 - <<'EOF'{"content":[{"text":"Carrier fees <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.…ctrl+o
cd /tmp && python3 - <<'EOF'
import re,html
t=open('s.html',encoding='utf-8',errors='ignore').read()
t=re.sub(r'<(script|style).*?</\1>','',t,flags=re.S)
t=html.unescape(re.sub(r'<[^>]+>',' ',t)); t=re.sub(r'\s+',' ',t)
i=t.rfind('Carrier fees')
print(t[i:i+900])
EOF

{"content":[{"text":"Carrier fees <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.3021M75.3037 3.98207L35.951 3.59351\" stroke=\"#F22F46\" stroke-width=\"5.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/> </svg> \"> There are additional carrier fees per message that are automatically applied based on the end-user's destination and carrier. Twilio helps by handling the carrier nuances in every country you send to. RCS Carrier RCS Rich Outbound* RCS Rich Inbound* RCS Rich Media Outbound* RCS Rich Media Inbound* AT&T $0.0045 $0.0045 $0.01 $0.01 T-Mobile $0.0062 $0.0025 $0.0125 $0.0125 Verizon $0.0045 $0 $0.007 $0 US Cellular $0.0062 $0.0025 $0.0135 $0.0135 All other carriers $0.0045 $0.0045 $0.01 $0.01 <title>An icon of a outbound link arrow</title> <path class=\"icon-stroke\" d=\"M75.3037 3.98207L3 75.5935M75.3037 3.98207L76.0435 43.\n","type":"text"}]}
Thinking…

Good. Now let me write the README with pricing, and add a requirements.txt. Then verify the whole thing.

Let me write the README.

Now let me write the README with the verified pricing and add requirements.

write /sandbox/repo/twilio_sms/README.md{"content":[{"text":"Successfully wrote 5536 bytes to /sandbox/repo/twilio_sms/README.md","type":"text"}]}
write /sandbox/repo/twilio_sms/requirements.txt{"content":[{"text":"Successfully wrote 17 bytes to /sandbox/repo/twilio_sms/requirements.txt","type":"text"}]}
Thinking…

Let me verify the claim about error codes 21211 and 21612 — I should be careful. Let me verify via Twilio's error docs quickly. Actually, let me verify these codes are real to avoid stating something wrong.

Let me check Twilio's error reference.

Also I should make sure .env is gitignored. Let me check the .gitignore — it doesn't have .env. Let me add it to avoid committing secrets.

Let me do both.

Let me verify the error codes I cited and protect .env from being committed.

$ cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" "https://www.twilio.com/docs/api/errors" -o e.html -w "http:%{http_code}\n"; for c in 20003 21211 21612; do printf "%s: " $c; grep -o "$c" e.html | head -1 |…{"content":[{"text":"http:200\n20003: 20003\n21211: 21211\n21612: 21612\n","type":"text"}]}ctrl+o
cd /tmp && curl -sL -m 20 -A "Mozilla/5.0" "https://www.twilio.com/docs/api/errors" -o e.html -w "http:%{http_code}\n"; for c in 20003 21211 21612; do printf "%s: " $c; grep -o "$c" e.html | head -1 || echo "NOT FOUND"; done

$ cd /sandbox/repo && grep -qE '^\.env$' .gitignore || printf '\n# Secrets / local config\n.env\n' >> .gitignore; tail -4 .gitignore{"content":[{"text":".dart_tool/\n\n# Secrets / local config\n.env\n","type":"text"}]}
Thinking…

All three error codes are confirmed on Twilio's official error reference page. And .env is now gitignored.

Let me do a final check of the whole thing — confirm files, and re-run the wiring test one final time to confirm everything is consistent.

Let me verify final state.

All three error codes confirmed against Twilio's official error reference, and .env is now gitignored. Final check:

$ cd /sandbox/repo/twilio_sms && ls -la && echo "=== syntax ===" && python3 -m py_compile send_sms.py && echo "send_sms.py OK" && echo "=== live wiring (fake creds, nothing sent) ===" && TWILIO_ACCOUNT_…{"content":[{"text":"total 28\ndrwx------ 2 root root 4096 Sep 29 08:17 .\ndrwxr-xr-x 4 root root 4096 Sep 29 08:16 ..\n-rw------- 1 root root 365 Sep 29 08:16…ctrl+o
cd /sandbox/repo/twilio_sms && ls -la && echo "=== syntax ===" && python3 -m py_compile send_sms.py && echo "send_sms.py OK" && echo "=== live wiring (fake creds, nothing sent) ===" && TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000 TWILIO_AUTH_TOKEN=fake TWILIO_FROM_NUMBER=+15551234567 TWILIO_TO_NUMBER=+15559876543 python3 send_sms.py "wiring test"; echo "exit=$?"; cd /sandbox/repo && git status --short

{"content":[{"text":"total 28\ndrwx------ 2 root root 4096 Sep 29 08:17 .\ndrwxr-xr-x 4 root root 4096 Sep 29 08:16 ..\n-rw------- 1 root root  365 Sep 29 08:16 .env.example\n-rw------- 1 root root 5568 Sep 29 08:17 README.md\n-rw------- 1 root root   17 Sep 29 08:17 requirements.txt\n-rw------- 1 root root 1742 Sep 29 08:16 send_sms.py\n=== syntax ===\nsend_sms.py OK\n=== live wiring (fake creds, nothing sent) ===\nTwilio rejected the request (20003): Unable to create record: auth account AC00000000000000000000000000000000 does not exist\nexit=1\n M .gitignore\n?? twilio_sms/\n","type":"text"}]}
Thinking…

Everything works. Let me clean up the pycache that py_compile created — actually .gitignore covers pycache, so it's fine. Let me remove it anyway to keep things tidy.

Let me finalize and give the summary.

$ cd /sandbox/repo/twilio_sms && rm -rf __pycache__ && ls{"content":[{"text":"README.md\nrequirements.txt\nsend_sms.py\n","type":"text"}]}
Thinking…

The .env.example is missing from ls because ls without -a doesn't show hidden files. That's fine.

Let me write the summary.

I should be honest about what's blocked: I can't actually send a message because there are no credentials. But the developer workflow is fully actionable — I built and verified it. So this is not a "stop" case.

Let me summarize concisely.

Done. Twilio has a real developer workflow, so I built it out rather than stopping.

What I built

/sandbox/repo/twilio_sms/ — a minimal working example:

FilePurpose
send_sms.pySends one SMS via the official twilio SDK
.env.exampleTemplate for the 4 required values
requirements.txtPins twilio>=9.11,<10
README.mdSetup, pricing, manageability

I also added .env to the repo .gitignore so credentials can't be committed.

Verified, not just written

I ran the script against Twilio's live API with deliberately fake credentials. Twilio returned:

Twilio rejected the request (20003): auth account AC0000... does not exist

That's the auth-failure code specifically — confirming the request was correctly formed, signed, and routed, and rejected only because the account doesn't exist. The error-handling path and the missing-env-var guard both tested clean. Nothing was sent, nothing billed.

One genuine block: I can't actually deliver a message. There are no Twilio credentials in this environment, and Twilio has no offline mode that accepts messages — a real account SID/auth token is the only way past 20003. Only you can create that.

How pricing works

Prepaid, pay-as-you-go. No contract, no subscription for the API. Three independent components — that's the key mental model. I scraped these from Twilio's live US pricing page rather than recalling them:

  • Per-message, charged per segment: US SMS $0.0083 out / $0.0083 in; MMS $0.022 out
  • Number rental, monthly: long code $1.15/mo ($0.50 bring-your-own), toll-free $2.15/mo
  • Carrier fees, added per message by recipient carrier; US A2P 10DLC adds one-time registration fees; $0.001 fee on Failed messages

Ballpark: one short US SMS ≈ $0.008–0.012. One long code sending 1,000 short messages/month ≈ $10–12.

Two gotchas worth flagging: segment billing (a 400-char message with an emoji is 6 segments, not 1 — UCS-2 splits at 70 chars), and the monthly rental bills even if you send nothing.

Manageability

Easy. One authenticated HTTPS call to a hosted API — no servers, containers, queues, or databases. Nothing long-running; the script exits immediately. Stateless, one dependency, and machine-readable error codes (20003, 21211, 21612 — I verified all three against Twilio's error reference) surfaced directly rather than swallowed.

The non-automatable overhead is account setup, buying a number, and US A2P 10DLC registration — a manual compliance process with carrier approval delays, unavoidable for production US traffic.

One recommendation before any unattended run: set a spend cap in Console (Settings → Usage → Triggers). It's the only real guard against a looped script silently draining credit.

AX Fundamentals

23 checks. Open an area for details.

This check is a quick preview. Test more tasks and models with Gauge Agents.

Test your product with Gauge Agents

AX Check by Gauge