- name
- twilio-api
- description
- Use this skill when working with Twilio communication APIs for SMS/MMS messaging, voice calls, phone number management, TwiML, webhook integration, two-way SMS conversations, bulk sending, or production deployment of telephony features. Includes official Twilio patterns, production code examples from Twilio-Aldea (provider-agnostic webhooks, signature validation, TwiML responses), and comprehensive TypeScript examples.
# Twilio API - Comprehensive Communication Platform
## When to Use This Skill
Use this skill when working with Twilio's communication APIs for:
- **SMS/MMS Messaging** - Send and receive text messages programmatically
- **Voice Communication** - Build voice calling applications with TwiML
- **Phone Number Management** - Search, purchase, and configure phone numbers
- **Webhook Integration** - Handle real-time events and delivery notifications with TwiML responses
- **Two-Way SMS Conversations** - Build interactive SMS experiences
- **Bulk SMS Sending** - Send messages to multiple recipients with rate limiting
- **Message Scheduling** - Schedule messages for future delivery
- **Production Deployment** - Deploy messaging features with error handling and monitoring
- **A2P 10DLC Registration** - Register brands and campaigns for US A2P messaging compliance
- **Provider-Agnostic Architecture** - Build systems that support multiple SMS providers (Twilio + Telnyx)
This skill applies to building communication features in applications, setting up SMS notification systems, creating voice IVR systems, or integrating telephony capabilities.
## Quick Reference
### 1. Send Simple SMS (Node.js SDK)
```javascript
const twilio = require('twilio');
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
async function sendSMS(to, from, body) {
const message = await client.messages.create({
to: to,
from: from,
body: body
});
return message;
}
// Usage
await sendSMS('+14155552671', '+14155559999', 'Hello from Twilio!');
```
### 2. Send SMS with HTTP (No SDK)
```javascript
const https = require('https');
function sendSMS(to, from, body) {
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const auth = Buffer.from(`${accountSid}:${authToken}`).toString('base64');
const postData = new URLSearchParams({
To: to,
From: from,
Body: body
}).toString();
const options = {
hostname: 'api.twilio.com',
port: 443,
path: `/2010-04-01/Accounts/${accountSid}/Messages.json`,
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postData.length
}
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => resolve(JSON.parse(data)));
});
req.on('error', reject);
req.write(postData);
req.end();
});
}
```
### 3. Validate Phone Numbers (E.164 Format)
```javascript
function validateE164(phoneNumber) {
const e164Regex = /^\+[1-9]\d{1,14}$/;
if (!e164Regex.test(phoneNumber)) {
return {
valid: false,
error: 'Phone number must be in E.164 format (e.g., +14155552671)'
};
}
return { valid: true };
}
// Normalize US phone numbers to E.164
function formatToE164(number) {
let digits = number.replace(/\D/g, '');
if (!digits.startsWith('1')) {
digits = '1' + digits;
}
return '+' + digits;
}
```
### 4. Handle Incoming Messages (Webhook with TwiML)
```javascript
const express = require('express');
app.use(express.urlencoded({ extended: false }));
app.post('/webhooks/twilio', (req, res) => {
const from = req.body.From;
const body = req.body.Body;
const to = req.body.To;
console.log(`Received: "${body}" from ${from}`);
// Respond with TwiML
const twiml = `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>Thanks for your message!</Message>
</Response>`;
res.set('Content-Type', 'text/xml');
res.send(twiml);
});
```
### 5. Verify Webhook Signatures (HMAC-SHA1)
```javascript
const crypto = require('crypto');
function verifyTwilioSignature(url, params, signature, authToken) {
// Build data string from sorted params
const data = Object.keys(params)
.sort()
.reduce((acc, key) => acc + key + params[key], url);
// Generate HMAC-SHA1 signature
const expectedSignature = crypto
.createHmac('sha1', authToken)
.update(Buffer.from(data, 'utf-8'))
.digest('base64');
return signature === expectedSignature;
}
// Usage in Express with body-parser
app.post('/webhooks/twilio', (req, res) => {
const signature = req.headers['x-twilio-signature'];
const url = `https://${req.headers.host}${req.url}`;
if (!verifyTwilioSignature(url, req.body, signature, process.env.TWILIO_AUTH_TOKEN)) {
return res.status(403).send('Forbidden');
}
// Process webhook...
const twiml = '<Response></Response>';
res.set('Content-Type', 'text/xml');
res.send(twiml);
});
```
### 6. Twilio SDK Signature Validation
```javascript
const twilio = require('twilio');
app.post('/webhooks/twilio', (req, res) => {
const signature = req.headers['x-twilio-signature'];
const url = `https://${req.headers.host}${req.url}`;
if (!twilio.validateRequest(
process.env.TWILIO_AUTH_TOKEN,
signature,
url,
req.body
)) {
return res.status(403).send('Forbidden');
}
// Process webhook...
const twiml = new twilio.twiml.MessagingResponse();
twiml.message('Thanks for your message!');
res.set('Content-Type', 'text/xml');
res.send(twiml.toString());
});
```
### 7. Send with Error Handling and Retry
```javascript
async function sendWithRetry(to, from, body, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client.messages.create({ to, from, body });
} catch (error) {
if (error.status >= 500 && attempt < maxRetries) {
// Server error - retry with exponential backoff
const delayMs = Math.pow(2, attempt) * 1000;
console.log(`Retry ${attempt} in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
} else {
throw error;
}
}
}
}
```
### 8. Bulk Sending with Rate Limiting
```javascript
async function sendBulkSMS(recipients, from, body) {
const delayMs = 100; // 10 messages/second
const results = [];
for (const recipient of recipients) {
try {
const result = await client.messages.create({ to: recipient, from, body });
results.push({ success: true, to: recipient, sid: result.sid });
} catch (error) {
results.push({ success: false, to: recipient, error: error.message });
}
await new Promise(resolve => setTimeout(resolve, delayMs));
}
return results;
}
```
### 9. Provider-Agnostic Webhook Handler (Twilio + Telnyx)
```typescript
// From Twilio-Aldea production codebase
function detectProvider(payload: any): 'twilio' | 'telnyx' {
// Telnyx uses JSON with data.event_type
if (payload.data && payload.data.event_type) {
return 'telnyx';
}
// Twilio uses form-urlencoded with MessageSid
if (payload.MessageSid || payload.From) {
return 'twilio';
}
throw new Error('Unknown SMS provider');
}
// Unified webhook handler
app.post('/api/sms/webhook', async (req, res) => {
const providerType = detectProvider(req.body);
if (providerType === 'twilio') {
// Validate Twilio signature
// Return TwiML response
const twiml = '<?xml version="1.0"?><Response></Response>';
res.set('Content-Type', 'text/xml');
res.send(twiml);
} else {
// Validate Telnyx Ed25519 signature
// Return JSON response
res.status(200).json({ status: 'ok' });
}
});
```
### 10. Handle Common Errors
```javascript
function handleTwilioError(error) {
if (!error.status) {
return { type: 'NETWORK_ERROR', retriable: true };
}
switch (error.status) {
case 400:
case 422:
// Validation error
return {
type: 'VALIDATION_ERROR',
message: error.message,
code: error.code,
retriable: false
};
case 401:
// Check Account SID and Auth Token
return { type: 'AUTH_ERROR', retriable: false };
case 429:
// Rate limit
return {
type: 'RATE_LIMIT',
retriable: true,
retryAfter: 60
};
case 500:
case 502:
case 503:
// Server error
return { type: 'SERVER_ERROR', retriable: true };
default:
return { type: 'UNKNOWN_ERROR', retriable: false };
}
}
```
## Key Concepts
### 1. E.164 Phone Number Format
International phone number format: `+[country code][number]`
- US Example: `+14155552671`
- UK Example: `+442071234567`
- Always include the `+` prefix
- Maximum 15 digits (excluding +)
### 2. Authentication (Basic Auth)
Twilio uses HTTP Basic Authentication with Account SID as username and Auth Token as password:
```
Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)
```
### 3. TwiML (Twilio Markup Language)
XML-based response format for webhooks:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>Your message text here</Message>
</Response>
```
Common TwiML verbs:
- `<Message>` - Send SMS/MMS reply
- `<Redirect>` - Redirect to another URL
- `<Dial>` - Make voice call
- `<Say>` - Text-to-speech
- `<Play>` - Play audio file
### 4. Webhook Events
Twilio sends form-urlencoded POST requests with:
- `MessageSid` - Unique message identifier
- `From` - Sender phone number
- `To` - Recipient phone number
- `Body` - Message text
- `MessageStatus` - Message status (queued, sent, delivered, failed, undelivered)
- `NumMedia` - Number of media attachments (MMS)
### 5. Message Status Lifecycle
- `queued` - Message accepted by Twilio
- `sending` - Being sent to carrier
- `sent` - Sent to carrier
- `delivered` - Delivered to recipient (requires StatusCallback)
- `undelivered` - Failed to deliver
- `failed` - Permanent failure
### 6. Signature Validation (HMAC-SHA1)
Twilio signs webhooks with HMAC-SHA1:
1. Concatenate URL + sorted parameters
2. Generate HMAC-SHA1 with Auth Token as key
3. Base64 encode the result
4. Compare with `X-Twilio-Signature` header
### 7. A2P 10DLC Registration
For US messaging, register:
1. **Brand** - Your business entity
2. **Campaign** - Use case (Customer Care, Marketing, 2FA, etc.)
3. **Phone Numbers** - Associate numbers with campaign
**Timeline**: 5-7 business days for approval
### 8. Message Encoding and Segmentation
- **GSM-7**: 160 chars/segment for standard ASCII
- **UCS-2**: 70 chars/segment for emoji/unicode
- Long messages split into segments (max 10)
- Multi-part: GSM-7 = 153 chars/segment, UCS-2 = 67 chars/segment
## Production Patterns from Twilio-Aldea
### Pattern 1: Provider-Agnostic Webhook Architecture
```typescript
// Support both Twilio and Telnyx from single endpoint
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const rawBody = await readRawBody(req);
// Auto-detect provider
let payload: any;
try {
payload = JSON.parse(rawBody); // Telnyx
} catch {
payload = parseFormUrlEncoded(rawBody); // Twilio
}
GitHubで見る