twilio-api
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTwilio API - Comprehensive Communication Platform
Twilio API - 全功能通信平台
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.
当你使用Twilio通信API处理以下场景时,可使用此技能:
- SMS/MMS消息 - 以编程方式发送和接收短信/彩信
- 语音通信 - 使用TwiML构建语音通话应用
- 电话号码管理 - 搜索、购买和配置电话号码
- Webhook集成 - 通过TwiML响应处理实时事件和送达通知
- 双向SMS对话 - 构建交互式短信体验
- 批量SMS发送 - 带速率限制的多收件人消息发送
- 消息调度 - 安排消息在未来时间发送
- 生产部署 - 部署带有错误处理和监控的消息功能
- A2P 10DLC注册 - 为美国A2P消息合规注册品牌和营销活动
- 多服务商兼容架构 - 构建支持多个SMS服务商(Twilio + Telnyx)的系统
此技能适用于在应用中构建通信功能、搭建SMS通知系统、创建语音IVR系统,或集成电话通信能力。
Quick Reference
快速参考
1. Send Simple SMS (Node.js SDK)
1. 发送简单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!');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)
2. 不使用SDK发送SMS(HTTP方式)
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();
});
}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)
3. 验证电话号码(E.164格式)
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;
}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 };
}
// 将美国电话号码标准化为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)
4. 处理 incoming 消息(带TwiML的Webhook)
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);
});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}`);
// 以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)
5. 验证Webhook签名(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);
});javascript
const crypto = require('crypto');
function verifyTwilioSignature(url, params, signature, authToken) {
// 从排序后的参数构建数据字符串
const data = Object.keys(params)
.sort()
.reduce((acc, key) => acc + key + params[key], url);
// 生成HMAC-SHA1签名
const expectedSignature = crypto
.createHmac('sha1', authToken)
.update(Buffer.from(data, 'utf-8'))
.digest('base64');
return signature === expectedSignature;
}
// 在Express中结合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');
}
// 处理Webhook...
const twiml = '<Response></Response>';
res.set('Content-Type', 'text/xml');
res.send(twiml);
});6. Twilio SDK Signature Validation
6. 使用Twilio SDK验证签名
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());
});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');
}
// 处理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
7. 带错误处理和重试的消息发送
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;
}
}
}
}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) {
// 服务器错误 - 指数退避重试
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
8. 带速率限制的批量发送
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;
}javascript
async function sendBulkSMS(recipients, from, body) {
const delayMs = 100; // 10条消息/秒
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)
9. 多服务商兼容的Webhook处理器(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' });
}
});typescript
// 来自Twilio-Aldea生产代码库
function detectProvider(payload: any): 'twilio' | 'telnyx' {
// Telnyx使用带data.event_type的JSON格式
if (payload.data && payload.data.event_type) {
return 'telnyx';
}
// Twilio使用带MessageSid的form-urlencoded格式
if (payload.MessageSid || payload.From) {
return 'twilio';
}
throw new Error('Unknown SMS provider');
}
// 统一Webhook处理器
app.post('/api/sms/webhook', async (req, res) => {
const providerType = detectProvider(req.body);
if (providerType === 'twilio') {
// 验证Twilio签名
// 返回TwiML响应
const twiml = '<?xml version="1.0"?><Response></Response>';
res.set('Content-Type', 'text/xml');
res.send(twiml);
} else {
// 验证Telnyx Ed25519签名
// 返回JSON响应
res.status(200).json({ status: 'ok' });
}
});10. Handle Common Errors
10. 处理常见错误
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 };
}
}javascript
function handleTwilioError(error) {
if (!error.status) {
return { type: 'NETWORK_ERROR', retriable: true };
}
switch (error.status) {
case 400:
case 422:
// 验证错误
return {
type: 'VALIDATION_ERROR',
message: error.message,
code: error.code,
retriable: false
};
case 401:
// 检查Account SID和Auth Token
return { type: 'AUTH_ERROR', retriable: false };
case 429:
// 速率限制
return {
type: 'RATE_LIMIT',
retriable: true,
retryAfter: 60
};
case 500:
case 502:
case 503:
// 服务器错误
return { type: 'SERVER_ERROR', retriable: true };
default:
return { type: 'UNKNOWN_ERROR', retriable: false };
}
}Key Concepts
核心概念
1. E.164 Phone Number Format
1. E.164电话号码格式
International phone number format:
+[country code][number]- US Example:
+14155552671 - UK Example:
+442071234567 - Always include the prefix
+ - Maximum 15 digits (excluding +)
国际标准电话号码格式:
+[国家代码][号码]- 美国示例:
+14155552671 - 英国示例:
+442071234567 - 必须包含前缀
+ - 最多15位数字(不含+)
2. Authentication (Basic Auth)
2. 身份验证(Basic Auth)
Twilio uses HTTP Basic Authentication with Account SID as username and Auth Token as password:
Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)Twilio使用HTTP基础身份验证,用户名是Account SID,密码是Auth Token:
Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)3. TwiML (Twilio Markup Language)
3. TwiML(Twilio标记语言)
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:
- - Send SMS/MMS reply
<Message> - - Redirect to another URL
<Redirect> - - Make voice call
<Dial> - - Text-to-speech
<Say> - - Play audio file
<Play>
用于Webhook的XML格式响应:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>Your message text here</Message>
</Response>常用TwiML动词:
- - 发送SMS/MMS回复
<Message> - - 重定向到其他URL
<Redirect> - - 发起语音通话
<Dial> - - 文本转语音
<Say> - - 播放音频文件
<Play>
4. Webhook Events
4. Webhook事件
Twilio sends form-urlencoded POST requests with:
- - Unique message identifier
MessageSid - - Sender phone number
From - - Recipient phone number
To - - Message text
Body - - Message status (queued, sent, delivered, failed, undelivered)
MessageStatus - - Number of media attachments (MMS)
NumMedia
Twilio发送form-urlencoded格式的POST请求,包含以下字段:
- - 唯一消息标识符
MessageSid - - 发送方电话号码
From - - 接收方电话号码
To - - 消息文本
Body - - 消息状态(queued、sent、delivered、failed、undelivered)
MessageStatus - - 媒体附件数量(MMS)
NumMedia
5. Message Status Lifecycle
5. 消息状态生命周期
- - Message accepted by Twilio
queued - - Being sent to carrier
sending - - Sent to carrier
sent - - Delivered to recipient (requires StatusCallback)
delivered - - Failed to deliver
undelivered - - Permanent failure
failed
- - 消息已被Twilio接收
queued - - 正在发送到运营商
sending - - 已发送到运营商
sent - - 已送达接收方(需要配置StatusCallback)
delivered - - 送达失败
undelivered - - 永久发送失败
failed
6. Signature Validation (HMAC-SHA1)
6. 签名验证(HMAC-SHA1)
Twilio signs webhooks with HMAC-SHA1:
- Concatenate URL + sorted parameters
- Generate HMAC-SHA1 with Auth Token as key
- Base64 encode the result
- Compare with header
X-Twilio-Signature
Twilio使用HMAC-SHA1为Webhook签名:
- 拼接URL + 排序后的参数
- 使用Auth Token作为密钥生成HMAC-SHA1签名
- 对结果进行Base64编码
- 与头信息对比
X-Twilio-Signature
7. A2P 10DLC Registration
7. A2P 10DLC注册
For US messaging, register:
- Brand - Your business entity
- Campaign - Use case (Customer Care, Marketing, 2FA, etc.)
- Phone Numbers - Associate numbers with campaign
Timeline: 5-7 business days for approval
针对美国地区的消息发送,需要注册:
- 品牌 - 你的商业实体
- 营销活动 - 使用场景(客户服务、营销、双因素认证等)
- 电话号码 - 将号码与营销活动关联
审核周期:5-7个工作日
8. Message Encoding and Segmentation
8. 消息编码与分段
- 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
- GSM-7:标准ASCII字符,每段160个字符
- UCS-2:表情符号/Unicode字符,每段70个字符
- 长消息会被拆分为多个分段(最多10段)
- 多段消息:GSM-7每段153字符,UCS-2每段67字符
Production Patterns from Twilio-Aldea
来自Twilio-Aldea的生产实践模式
Pattern 1: Provider-Agnostic Webhook Architecture
模式1:多服务商兼容的Webhook架构
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
}
const providerType = detectProvider(payload);
const provider = getProviderByType(providerType);
// Validate signature
const isValid = provider.validateSignature(req, rawBody);
if (!isValid) {
return res.status(403).json({ error: 'Invalid signature' });
}
// Process message
await processIncomingSMS(payload, provider);
// Return provider-specific response
if (providerType === 'twilio') {
res.set('Content-Type', 'text/xml');
res.send('<?xml version="1.0"?><Response></Response>');
} else {
res.status(200).json({ status: 'ok' });
}
}typescript
// 从单个端点同时支持Twilio和Telnyx
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const rawBody = await readRawBody(req);
// 自动检测服务商
let payload: any;
try {
payload = JSON.parse(rawBody); // Telnyx格式
} catch {
payload = parseFormUrlEncoded(rawBody); // Twilio格式
}
const providerType = detectProvider(payload);
const provider = getProviderByType(providerType);
// 验证签名
const isValid = provider.validateSignature(req, rawBody);
if (!isValid) {
return res.status(403).json({ error: 'Invalid signature' });
}
// 处理消息
await processIncomingSMS(payload, provider);
// 返回服务商特定的响应
if (providerType === 'twilio') {
res.set('Content-Type', 'text/xml');
res.send('<?xml version="1.0"?><Response></Response>');
} else {
res.status(200).json({ status: 'ok' });
}
}Pattern 2: Raw Body Preservation for Signature Validation
模式2:保留原始请求体用于签名验证
typescript
// Next.js API route config
export const config = {
api: {
bodyParser: false, // Preserve raw body
},
};
async function readRawBody(req: NextApiRequest): Promise<string> {
return new Promise<string>((resolve, reject) => {
let data = '';
req.setEncoding('utf8');
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => resolve(data));
req.on('error', reject);
});
}typescript
// Next.js API路由配置
export const config = {
api: {
bodyParser: false, // 保留原始请求体
},
};
async function readRawBody(req: NextApiRequest): Promise<string> {
return new Promise<string>((resolve, reject) => {
let data = '';
req.setEncoding('utf8');
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => resolve(data));
req.on('error', reject);
});
}Pattern 3: Fast Mode vs Compute Mode
模式3:快速模式vs计算模式
typescript
// Environment variable: SMS_FAST_MODE=true/false
const fastMode = process.env.SMS_FAST_MODE?.toLowerCase() !== 'false';
if (fastMode) {
// Return immediate acknowledgment
res.status(200).send(twiml);
// Process async in background
processIncomingSMS(payload).catch(console.error);
} else {
// Wait for AI processing
await processIncomingSMS(payload);
res.status(200).send(twiml);
}typescript
// 环境变量:SMS_FAST_MODE=true/false
const fastMode = process.env.SMS_FAST_MODE?.toLowerCase() !== 'false';
if (fastMode) {
// 立即返回确认响应
res.status(200).send(twiml);
// 在后台异步处理
processIncomingSMS(payload).catch(console.error);
} else {
// 等待AI处理完成
await processIncomingSMS(payload);
res.status(200).send(twiml);
}Pattern 4: TwiML Response Builder
模式4:TwiML响应构建器
typescript
function buildTwiMLResponse(message?: string): string {
if (!message) {
return '<?xml version="1.0" encoding="UTF-8"?><Response></Response>';
}
// Escape XML special characters
const escaped = message
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
return `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>${escaped}</Message>
</Response>`;
}typescript
function buildTwiMLResponse(message?: string): string {
if (!message) {
return '<?xml version="1.0" encoding="UTF-8"?><Response></Response>';
}
// 转义XML特殊字符
const escaped = message
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
return `<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>${escaped}</Message>
</Response>`;
}Pattern 5: Idempotency with Database
模式5:基于数据库的幂等性处理
typescript
// PostgreSQL with unique constraint on message_sid
async function processWebhookIdempotent(messageSid: string, client: any) {
try {
await client.query('BEGIN');
await client.query(
'INSERT INTO processed_webhooks (message_sid, processed_at) VALUES ($1, NOW())',
[messageSid]
);
await handleMessage(messageSid, client);
await client.query('COMMIT');
} catch (error: any) {
await client.query('ROLLBACK');
if (error.code === '23505') { // Duplicate key
console.log('Message already processed');
return;
}
throw error;
}
}typescript
// PostgreSQL对message_sid设置唯一约束
async function processWebhookIdempotent(messageSid: string, client: any) {
try {
await client.query('BEGIN');
await client.query(
'INSERT INTO processed_webhooks (message_sid, processed_at) VALUES ($1, NOW())',
[messageSid]
);
await handleMessage(messageSid, client);
await client.query('COMMIT');
} catch (error: any) {
await client.query('ROLLBACK');
if (error.code === '23505') { // 重复键错误
console.log('Message already processed');
return;
}
throw error;
}
}Pattern 6: Timeout Protection
模式6:超时保护
typescript
function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number = 25000
): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeoutMs)
),
]);
}
// Usage
const result = await withTimeout(
processIncomingSMS(payload),
25000
);typescript
function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number = 25000
): Promise<T> {
return Promise.race([
promise,
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeoutMs)
),
]);
}
// 使用示例
const result = await withTimeout(
processIncomingSMS(payload),
25000
);API Essentials
API基础信息
Base URL
基础URL
https://api.twilio.com/2010-04-01https://api.twilio.com/2010-04-01Authentication
身份验证
Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)Environment Variables
环境变量
bash
undefinedbash
undefined.env file
.env文件
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_PHONE_NUMBER=+18005551234
undefinedTWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_PHONE_NUMBER=+18005551234
undefinedRate Limits
速率限制
- Messaging: 200 messages per second (enterprise)
- Voice: 100 concurrent calls (default)
- API requests: 10,000 per hour (default)
- 消息发送:200条/秒(企业版)
- 语音通话:100路并发(默认)
- API请求:10,000次/小时(默认)
Common Response Structure
通用响应结构
json
{
"sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"date_created": "Wed, 18 Aug 2021 20:01:14 +0000",
"date_updated": "Wed, 18 Aug 2021 20:01:14 +0000",
"date_sent": null,
"account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"to": "+14155552671",
"from": "+14155559999",
"body": "Hello from Twilio!",
"status": "queued",
"num_segments": "1",
"num_media": "0",
"direction": "outbound-api",
"price": null,
"price_unit": "USD",
"uri": "/2010-04-01/Accounts/ACxxx/Messages/SMxxx.json"
}json
{
"sid": "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"date_created": "Wed, 18 Aug 2021 20:01:14 +0000",
"date_updated": "Wed, 18 Aug 2021 20:01:14 +0000",
"date_sent": null,
"account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"to": "+14155552671",
"from": "+14155559999",
"body": "Hello from Twilio!",
"status": "queued",
"num_segments": "1",
"num_media": "0",
"direction": "outbound-api",
"price": null,
"price_unit": "USD",
"uri": "/2010-04-01/Accounts/ACxxx/Messages/SMxxx.json"
}Quick Start Checklist
快速入门检查清单
- Sign up for Twilio account at https://www.twilio.com/try-twilio
- Get Account SID and Auth Token from Console
- Set up environment variables
- Purchase a phone number for testing
- Send your first test SMS (Quick Reference #1)
- Validate phone numbers (Quick Reference #3)
- Set up webhook endpoint (use ngrok for local dev)
- Implement webhook handler with TwiML (Quick Reference #4)
- Add webhook signature verification (Quick Reference #5 or #6)
- Test two-way messaging
- Add error handling with retry logic (Quick Reference #7)
- (US only) Register for A2P 10DLC if sending to US numbers
- 在https://www.twilio.com/try-twilio注册Twilio账号
- 从控制台获取Account SID和Auth Token
- 配置环境变量
- 购买测试用电话号码
- 发送你的第一条测试SMS(参考快速参考#1)
- 验证电话号码格式(参考快速参考#3)
- 搭建Webhook端点(本地开发可使用ngrok)
- 实现带TwiML的Webhook处理器(参考快速参考#4)
- 添加Webhook签名验证(参考快速参考#5或#6)
- 测试双向消息功能
- 添加带重试逻辑的错误处理(参考快速参考#7)
- [ ](仅美国地区)若向美国号码发送消息,注册A2P 10DLC
Working with This Skill
如何使用此技能
For Beginners
初学者指南
Start Here:
- Use Quick Reference #1 (Send Simple SMS)
- Set up environment variables
- Use Quick Reference #3 (Validate Phone Numbers)
- Test sending to your own phone number
- Set up Quick Reference #4 (Handle Incoming Messages with TwiML)
- Test two-way messaging with ngrok
Key Concepts to Learn:
- E.164 phone number format
- Basic Authentication (Account SID + Auth Token)
- TwiML XML responses for webhooks
- Message status lifecycle
Common Beginner Mistakes:
- Forgetting the prefix in phone numbers
+ - Not using E.164 format
- Hardcoding credentials instead of environment variables
- Not returning TwiML from webhook endpoints
- Not validating webhook signatures
入门步骤:
- 使用快速参考#1(发送简单SMS)
- 配置环境变量
- 使用快速参考#3(验证电话号码)
- 测试向自己的号码发送消息
- 搭建快速参考#4(带TwiML的消息接收处理)
- 使用ngrok测试双向消息功能
需要掌握的核心概念:
- E.164电话号码格式
- 基础身份验证(Account SID + Auth Token)
- 用于Webhook的TwiML XML响应
- 消息状态生命周期
常见初学者错误:
- 电话号码忘记添加前缀
+ - 未使用E.164格式
- 硬编码凭证而非使用环境变量
- Webhook端点未返回TwiML
- 未验证Webhook签名
For Intermediate Users
中级用户指南
Focus Areas:
- Implement Quick Reference #5 or #6 (Signature Validation)
- Use Quick Reference #7 (Error Handling with Retry)
- Build conversation flows with state machines
- Implement idempotency (Production Pattern #5)
- Handle StatusCallback webhooks for delivery notifications
Key Concepts to Master:
- HMAC-SHA1 signature validation
- TwiML advanced features
- Message segmentation and cost optimization
- Error handling patterns
- Rate limiting for bulk sending
重点关注:
- 实现快速参考#5或#6(签名验证)
- 使用快速参考#7(带重试的错误处理)
- 使用状态机构建对话流程
- 实现幂等性处理(生产实践模式#5)
- 处理StatusCallback Webhook以跟踪消息送达状态
需要掌握的核心概念:
- HMAC-SHA1签名验证
- TwiML高级功能
- 消息分段与成本优化
- 错误处理模式
- 批量发送的速率限制
For Advanced Users
高级用户指南
Advanced Patterns:
- Build provider-agnostic handlers (Production Pattern #1)
- Implement timeout protection (Production Pattern #6)
- Design multi-provider architectures
- Optimize with fast mode vs compute mode (Production Pattern #3)
- Build IVR systems with Voice API
- Set up comprehensive monitoring and alerting
Key Topics:
- Provider-agnostic webhook architecture
- Database-backed idempotency
- Structured logging and monitoring
- A2P 10DLC compliance
- Production deployment patterns
高级实践:
- 构建多服务商兼容的处理器(生产实践模式#1)
- 实现超时保护(生产实践模式#6)
- 设计多服务商架构
- 使用快速模式vs计算模式优化(生产实践模式#3)
- 使用Voice API构建IVR系统
- 搭建全面的监控和告警系统
核心主题:
- 多服务商兼容的Webhook架构
- 基于数据库的幂等性
- 结构化日志与监控
- A2P 10DLC合规
- 生产部署模式
Common Error Codes
常见错误码
Authentication Errors
身份验证错误
- - Authentication failed (check Account SID and Auth Token)
20003 - - Account not active
20005
- - 身份验证失败(检查Account SID和Auth Token)
20003 - - 账号未激活
20005
Validation Errors
验证错误
- - Invalid 'To' phone number
21211 - - Invalid 'From' phone number
21212 - - Permission to send to this number not enabled
21408 - - Attempt to send to unsubscribed recipient
21610
- - 无效的“To”电话号码
21211 - - 无效的“From”电话号码
21212 - - 无权限向该号码发送消息
21408 - - 尝试向退订用户发送消息
21610
Rate Limit Errors
速率限制错误
- - Too many requests (rate limited)
20429
- - 请求过多(触发速率限制)
20429
Message Errors
消息发送错误
- - Queue overflow (system overloaded)
30001 - - Unreachable destination
30003 - - Message blocked
30004 - - Unknown destination
30005 - - Landline or unreachable carrier
30006 - - Message filtered (spam)
30007 - - Unknown error
30008
- - 队列溢出(系统过载)
30001 - - 目标不可达
30003 - - 消息被拦截
30004 - - 未知目标
30005 - - 固定电话或不可达运营商
30006 - - 消息被过滤(标记为垃圾信息)
30007 - - 未知错误
30008
Best Practices
最佳实践
1. Always Validate Webhook Signatures
1. 始终验证Webhook签名
javascript
// Use Twilio SDK for built-in validation
const twilio = require('twilio');
if (!twilio.validateRequest(authToken, signature, url, params)) {
return res.status(403).send('Forbidden');
}javascript
// 使用Twilio SDK进行内置验证
const twilio = require('twilio');
if (!twilio.validateRequest(authToken, signature, url, params)) {
return res.status(403).send('Forbidden');
}2. Return TwiML Immediately
2. 立即返回TwiML响应
javascript
// Don't do expensive processing before responding
app.post('/webhook', async (req, res) => {
// Return TwiML immediately
res.set('Content-Type', 'text/xml');
res.send('<Response></Response>');
// Process async
processMessage(req.body).catch(console.error);
});javascript
// 不要在响应前执行耗时操作
app.post('/webhook', async (req, res) => {
// 立即返回TwiML
res.set('Content-Type', 'text/xml');
res.send('<Response></Response>');
// 异步处理
processMessage(req.body).catch(console.error);
});3. Use StatusCallback for Delivery Tracking
3. 使用StatusCallback跟踪送达状态
javascript
await client.messages.create({
to: '+14155552671',
from: '+14155559999',
body: 'Hello!',
statusCallback: 'https://yourdomain.com/status'
});javascript
await client.messages.create({
to: '+14155552671',
from: '+14155559999',
body: 'Hello!',
statusCallback: 'https://yourdomain.com/status'
});4. Handle Message Segmentation
4. 优化消息以适配GSM-7编码
javascript
// Keep messages under 160 characters for GSM-7
function optimizeForGSM7(text) {
return text
.replace(/[""]/g, '"')
.replace(/['']/g, "'")
.replace(/[—–]/g, '-')
.replace(/…/g, '...');
}javascript
// 将消息控制在160字符以内以适配GSM-7
function optimizeForGSM7(text) {
return text
.replace(/[""]/g, '"')
.replace(/['']/g, "'")
.replace(/[—–]/g, '-')
.replace(/…/g, '...');
}5. Implement Exponential Backoff
5. 实现指数退避重试
javascript
async function sendWithBackoff(to, from, body, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client.messages.create({ to, from, body });
} catch (error) {
if (attempt < maxRetries && error.status >= 500) {
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
} else {
throw error;
}
}
}
}javascript
async function sendWithBackoff(to, from, body, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client.messages.create({ to, from, body });
} catch (error) {
if (attempt < maxRetries && error.status >= 500) {
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
} else {
throw error;
}
}
}
}Resources
资源链接
- Twilio Console: https://console.twilio.com/
- API Reference: https://www.twilio.com/docs/api
- Helper Libraries: Node.js, Python, PHP, Ruby, C#, Java
- Status Page: https://status.twilio.com/
- Support: https://support.twilio.com/
- Twilio控制台:https://console.twilio.com/
- API参考文档:https://www.twilio.com/docs/api
- 辅助开发库:Node.js、Python、PHP、Ruby、C#、Java
- 状态页面:https://status.twilio.com/
- 支持中心:https://support.twilio.com/
Version Notes
版本说明
This skill includes:
- Official Twilio API patterns and best practices
- Production code examples from Twilio-Aldea SMS platform
- Provider-agnostic webhook architecture
- TwiML response patterns
- Complete signature validation examples
- TypeScript and JavaScript examples
此技能包含:
- Twilio官方API实践模式与最佳实践
- 来自Twilio-Aldea SMS平台的生产代码示例
- 多服务商兼容的Webhook架构
- TwiML响应模式
- 完整的签名验证示例
- TypeScript和JavaScript示例