# Twilio Sms

> Complete Twilio integration - SMS, Voice calls, WhatsApp messaging, and phone number management via REST API. Use for sending messages, making calls, managing phone numbers, and WhatsApp Business messaging. Bypasses broken Twilio MCP server.

- Skill: `shakudo-io/twilio-sms` (Agent Skill)
- Install (CLI): `npx skillmds@latest add shakudo-io/twilio-sms`
- Raw SKILL.md: https://api.skillmd.com/api/skills/shakudo-io/twilio-sms/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: MIT
- Author: Shakudo-io (https://skillmd.com/u/shakudo-io)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/shakudo-io/twilio-sms

---


# 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

```bash
# 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

```bash
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 secret
- `TWILIO_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 secret
- `TWILIO_WHATSAPP_NUMBER` - WhatsApp Business number (+17188652473)

### Authentication

All API calls use HTTP Basic Auth:
```bash
# 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

```bash
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:**
```json
{
  "sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "status": "queued",
  "to": "+1RECIPIENT_NUMBER",
  "from": "+15551234567",
  "body": "Your message text here"
}
```

## Check Message Status

```bash
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

```bash
# 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)

```bash
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)

```bash
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:**
```json
{
  "sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "status": "queued",
  "to": "+1RECIPIENT_NUMBER",
  "from": "+15551234567",
  "direction": "outbound-api"
}
```

## Check Call Status

```bash
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

```bash
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)

```xml
<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)

```xml
<Response>
  <Play>https://your-server.com/audio.mp3</Play>
</Response>
```

### Gather (Collect User Input)

```xml
<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

```xml
<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

1. **24-hour Session Window:** Free-form messages only within 24h of user's last message
2. **User Must Initiate First:** Recipient must message your WhatsApp number first to open a session
3. **Templates for Business-Initiated:** Outside 24h window, must use pre-approved templates
4. **Production Number:** `+17188652473` (Business name: "Shakudo")

## Send WhatsApp Message (Production)

```bash
# 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

```bash
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:

```bash
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 | WhatsApp |
|---------|-----|----------|
| 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)

```bash
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

```bash
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

```bash
# 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

```bash
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)

```bash
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

```bash
# 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:**
```json
{
  "sid": "PNxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "phone_number": "+14155552344",
  "friendly_name": "Customer Support",
  "capabilities": {"voice": true, "sms": true, "mms": true}
}
```

## Configure a Phone Number

```bash
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

```bash
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

```bash
# 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

```bash
# 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

```bash
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

```bash
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`

---

## Related Resources

- [Twilio REST API Docs](https://www.twilio.com/docs/usage/api)
- [TwiML Reference](https://www.twilio.com/docs/voice/twiml)
- [WhatsApp API](https://www.twilio.com/docs/whatsapp/api)
- [Error Codes](https://www.twilio.com/docs/api/errors)
- [Pricing](https://www.twilio.com/pricing)

