Twilio Skill
Complete Twilio REST API integration for SMS, Voice, WhatsApp, and Phone Number management.
Why This Skill Exists
The official @twilio-alpha/mcp package has malformed tool names (double dashes --) that violate MCP spec SEP-986. This skill bypasses the broken MCP server by calling Twilio's REST API directly.
Quick Start
# Load credentials (includes both main and WhatsApp accounts)
source /root/gitrepos/.claude/skills/twilio-sms/.env
# Send SMS (uses main account)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" \
-d "From=${TWILIO_PHONE_NUMBER}" -d "To=+1RECIPIENT" -d "Body=Hello!"
# Make a call (uses main account)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" \
-d "From=${TWILIO_PHONE_NUMBER}" -d "To=+1RECIPIENT" \
--data-urlencode "Twiml=<Response><Say>Hello from Twilio!</Say></Response>"
# Send WhatsApp (uses separate WhatsApp account - recipient must message you first!)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_WHATSAPP_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_WHATSAPP_API_KEY_SID}:${TWILIO_WHATSAPP_API_KEY_SECRET}" \
-d "From=whatsapp:${TWILIO_WHATSAPP_NUMBER}" -d "To=whatsapp:+1RECIPIENT" -d "Body=Hello!"
Prerequisites & Authentication
Credentials Location
source /root/gitrepos/.claude/skills/twilio-sms/.env
Dual Account Configuration
The .env contains TWO accounts:
Main Account (SMS, Voice, Numbers):
TWILIO_ACCOUNT_SID- Account identifier (AC7d33...)TWILIO_API_KEY_SID- API key (SK075...)TWILIO_API_KEY_SECRET- API secretTWILIO_PHONE_NUMBER- Default outbound number (+14422765842)
WhatsApp Account (Separate):
TWILIO_WHATSAPP_ACCOUNT_SID- WhatsApp account identifier (AC992...)TWILIO_WHATSAPP_API_KEY_SID- WhatsApp API key (SK0b3...)TWILIO_WHATSAPP_API_KEY_SECRET- WhatsApp API secretTWILIO_WHATSAPP_NUMBER- WhatsApp Business number (+17188652473)
Authentication
All API calls use HTTP Basic Auth:
# SMS/Voice/Numbers (main account)
curl -u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" ...
# WhatsApp (separate account)
curl -u "${TWILIO_WHATSAPP_API_KEY_SID}:${TWILIO_WHATSAPP_API_KEY_SECRET}" ...
Part 1: SMS Messaging
Send SMS
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" \
-d "From=${TWILIO_PHONE_NUMBER}" \
-d "To=+1RECIPIENT_NUMBER" \
-d "Body=Your message text here"
Response:
{
"sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "queued",
"to": "+1RECIPIENT_NUMBER",
"from": "+15551234567",
"body": "Your message text here"
}
Check Message Status
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages/${MESSAGE_SID}.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Statuses: queued → sending → sent → delivered (or failed/undelivered)
List Messages
# Last 20 messages
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages.json?PageSize=20" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
# Filter by recipient
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Messages.json?To=+1RECIPIENT" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
SMS Error Codes
| Code | Meaning | Fix |
|---|---|---|
| 21211 | Invalid 'To' number | Use E.164 format (+1XXXXXXXXXX) |
| 21212 | Invalid 'From' number | Use Twilio number you own |
| 21610 | Recipient unsubscribed | Remove from list (STOP received) |
| 30003 | Unreachable | Carrier issue, retry later |
| 30004 | Message blocked | Carrier filtered as spam |
Part 2: Voice Calls
Make an Outbound Call
With Inline TwiML (Simple)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" \
--data-urlencode "To=+1RECIPIENT_NUMBER" \
--data-urlencode "From=${TWILIO_PHONE_NUMBER}" \
--data-urlencode "Twiml=<Response><Say voice=\"Polly.Joanna\">Hello! This is an automated call from your system.</Say></Response>"
With TwiML URL (Complex Flows)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" \
--data-urlencode "To=+1RECIPIENT_NUMBER" \
--data-urlencode "From=${TWILIO_PHONE_NUMBER}" \
--data-urlencode "Url=https://your-server.com/twiml-endpoint"
Response:
{
"sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "queued",
"to": "+1RECIPIENT_NUMBER",
"from": "+15551234567",
"direction": "outbound-api"
}
Check Call Status
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls/${CALL_SID}.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Call Statuses:
| Status | Description |
|---|---|
queued |
Call ready, waiting to dial |
ringing |
Currently ringing |
in-progress |
Call answered and active |
completed |
Call ended normally |
busy |
Received busy signal |
no-answer |
No answer within timeout |
failed |
Could not complete (invalid number) |
canceled |
Canceled via API |
List Recent Calls
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json?PageSize=20" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
# Filter by status
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Calls.json?Status=completed" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
TwiML Reference
TwiML controls call flow. Common verbs:
Say (Text-to-Speech)
<Response>
<Say voice="Polly.Joanna" language="en-US">Hello, welcome to our service.</Say>
</Response>
Voices: Polly.Joanna, Polly.Matthew, Polly.Amy (UK), Google.en-US-Wavenet-F
Play (Audio File)
<Response>
<Play>https://your-server.com/audio.mp3</Play>
</Response>
Gather (Collect User Input)
<Response>
<Gather input="dtmf" numDigits="1" action="/handle-input" method="POST">
<Say>Press 1 for sales, 2 for support.</Say>
</Gather>
<Say>We didn't receive any input. Goodbye.</Say>
</Response>
Complete IVR Example
<Response>
<Gather input="dtmf speech" numDigits="1" timeout="5" action="/menu" method="POST">
<Say voice="Polly.Joanna">
Welcome to Acme Corporation.
Press 1 or say sales for our sales team.
Press 2 or say support for technical support.
Press 0 to speak with an operator.
</Say>
</Gather>
<Say>We didn't receive a response. Goodbye.</Say>
<Hangup/>
</Response>
Voice Error Codes
| Code | Meaning |
|---|---|
| 21201 | Invalid phone number format |
| 21210 | Unverified number (trial account) |
| 21214 | Number not voice-capable |
| 31002 | Invalid TwiML syntax |
Part 3: WhatsApp Messaging
Important: Dual Account Setup
This skill uses TWO separate Twilio accounts:
- Main Account (
TWILIO_*): SMS, Voice, Phone Numbers - WhatsApp Account (
TWILIO_WHATSAPP_*): WhatsApp Business only
WhatsApp uses a separate account with its own credentials. Always use the TWILIO_WHATSAPP_* environment variables for WhatsApp.
WhatsApp Business Requirements
- 24-hour Session Window: Free-form messages only within 24h of user's last message
- User Must Initiate First: Recipient must message your WhatsApp number first to open a session
- Templates for Business-Initiated: Outside 24h window, must use pre-approved templates
- Production Number:
+17188652473(Business name: "Shakudo")
Send WhatsApp Message (Production)
# IMPORTANT: Use WHATSAPP credentials (separate account)
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_WHATSAPP_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_WHATSAPP_API_KEY_SID}:${TWILIO_WHATSAPP_API_KEY_SECRET}" \
-d "From=whatsapp:${TWILIO_WHATSAPP_NUMBER}" \
-d "To=whatsapp:+1RECIPIENT_NUMBER" \
-d "Body=Hello from WhatsApp!"
Error 63016 "24h window"? The recipient must message your WhatsApp number first. Have them send any message to +17188652473, then retry.
Send WhatsApp with Media
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_WHATSAPP_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_WHATSAPP_API_KEY_SID}:${TWILIO_WHATSAPP_API_KEY_SECRET}" \
-d "From=whatsapp:${TWILIO_WHATSAPP_NUMBER}" \
-d "To=whatsapp:+1RECIPIENT_NUMBER" \
-d "Body=Check out this image!" \
-d "MediaUrl=https://example.com/image.jpg"
Supported media: Images (JPEG, PNG), Audio (MP3, OGG), Video (MP4), Documents (PDF)
Send WhatsApp Template (Outside 24h Window)
Templates are required for business-initiated messages outside the 24h session window:
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_WHATSAPP_ACCOUNT_SID}/Messages.json" \
-u "${TWILIO_WHATSAPP_API_KEY_SID}:${TWILIO_WHATSAPP_API_KEY_SECRET}" \
-d "From=whatsapp:${TWILIO_WHATSAPP_NUMBER}" \
-d "To=whatsapp:+1RECIPIENT" \
-d "ContentSid=HXXXXXXXXXXXXXXXXXXXXXXXXXXX"
WhatsApp Error Codes
| Code | Meaning | Fix |
|---|---|---|
| 63016 | 24h session window expired | Recipient must message you first |
| 63003 | Channel not found | Verify whatsapp: prefix on both numbers |
| 63007 | Rate limit | Wait and retry |
WhatsApp vs SMS Comparison
| Feature | SMS | |
|---|---|---|
| Number Format | +1234567890 |
whatsapp:+1234567890 |
| Media Support | MMS (limited) | Images, video, audio, docs |
| Template Required | No | Yes (business-initiated) |
| Read Receipts | No | Yes |
| End-to-End Encryption | No | Yes |
| Character Limit | 160 (or 1600 concatenated) | 4096 |
Part 4: Phone Number Management
Search Available Numbers
By Area Code (US)
curl -G "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/AvailablePhoneNumbers/US/Local.json" \
-d "AreaCode=415" \
-d "SmsEnabled=true" \
-d "VoiceEnabled=true" \
-d "PageSize=5" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
By State/Region
curl -G "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/AvailablePhoneNumbers/US/Local.json" \
-d "InRegion=CA" \
-d "SmsEnabled=true" \
-d "PageSize=5" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
By Pattern
# Contains "555"
curl -G "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/AvailablePhoneNumbers/US/Local.json" \
-d "Contains=555" \
-d "PageSize=5" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Toll-Free Numbers
curl -G "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/AvailablePhoneNumbers/US/TollFree.json" \
-d "SmsEnabled=true" \
-d "PageSize=5" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
International (UK Example)
curl -G "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/AvailablePhoneNumbers/GB/Local.json" \
-d "SmsEnabled=true" \
-d "VoiceEnabled=true" \
-d "PageSize=5" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Purchase a Phone Number
# Purchase specific number found in search
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/IncomingPhoneNumbers.json" \
--data-urlencode "PhoneNumber=+14155552344" \
--data-urlencode "FriendlyName=Customer Support" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Response:
{
"sid": "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"phone_number": "+14155552344",
"friendly_name": "Customer Support",
"capabilities": {"voice": true, "sms": true, "mms": true}
}
Configure a Phone Number
curl -X POST "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/IncomingPhoneNumbers/${PHONE_SID}.json" \
--data-urlencode "SmsUrl=https://your-app.com/sms-webhook" \
--data-urlencode "VoiceUrl=https://your-app.com/voice-webhook" \
--data-urlencode "FriendlyName=Support Line" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
List Your Phone Numbers
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/IncomingPhoneNumbers.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Release (Delete) a Phone Number
# WARNING: Cannot be undone - number may be reassigned to others
curl -X DELETE "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/IncomingPhoneNumbers/${PHONE_SID}.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Number Pricing
# Get US pricing
curl "https://pricing.twilio.com/v1/PhoneNumbers/Countries/US" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Typical Monthly Costs:
- US Local: ~$1.00/month
- US Toll-Free: ~$2.00/month
- UK Local: ~$1.00/month
Part 5: Account Management
Check Account Balance
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}/Balance.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}" | jq '{balance, currency}'
Get Account Info
curl "https://api.twilio.com/2010-04-01/Accounts/${TWILIO_ACCOUNT_SID}.json" \
-u "${TWILIO_API_KEY_SID}:${TWILIO_API_KEY_SECRET}"
Quick Reference
Endpoints Summary
| Action | Method | Endpoint |
|---|---|---|
| Send SMS/WhatsApp | POST | /Accounts/{sid}/Messages.json |
| Get Message | GET | /Accounts/{sid}/Messages/{msgSid}.json |
| Make Call | POST | /Accounts/{sid}/Calls.json |
| Get Call | GET | /Accounts/{sid}/Calls/{callSid}.json |
| Search Numbers | GET | /Accounts/{sid}/AvailablePhoneNumbers/{country}/Local.json |
| Buy Number | POST | /Accounts/{sid}/IncomingPhoneNumbers.json |
| List Numbers | GET | /Accounts/{sid}/IncomingPhoneNumbers.json |
| Delete Number | DELETE | /Accounts/{sid}/IncomingPhoneNumbers/{phoneSid}.json |
| Get Balance | GET | /Accounts/{sid}/Balance.json |
Phone Number Format (E.164)
| Country | Format | Example |
|---|---|---|
| US/Canada | +1XXXXXXXXXX | +15551234567 |
| UK | +44XXXXXXXXXX | +447911123456 |
| Germany | +49XXXXXXXXX | +4915123456789 |
| Australia | +61XXXXXXXXX | +61412345678 |
Country Codes for Number Search
US, CA, GB, AU, DE, FR, JP, IN, BR, MX