twilio-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Twilio 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:
  • <Message>
    - Send SMS/MMS reply
  • <Redirect>
    - Redirect to another URL
  • <Dial>
    - Make voice call
  • <Say>
    - Text-to-speech
  • <Play>
    - Play audio file
用于Webhook的XML格式响应:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Message>Your message text here</Message>
</Response>
常用TwiML动词:
  • <Message>
    - 发送SMS/MMS回复
  • <Redirect>
    - 重定向到其他URL
  • <Dial>
    - 发起语音通话
  • <Say>
    - 文本转语音
  • <Play>
    - 播放音频文件

4. Webhook Events

4. Webhook事件

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)
Twilio发送form-urlencoded格式的POST请求,包含以下字段:
  • MessageSid
    - 唯一消息标识符
  • From
    - 发送方电话号码
  • To
    - 接收方电话号码
  • Body
    - 消息文本
  • MessageStatus
    - 消息状态(queued、sent、delivered、failed、undelivered)
  • NumMedia
    - 媒体附件数量(MMS)

5. Message Status Lifecycle

5. 消息状态生命周期

  • 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
  • queued
    - 消息已被Twilio接收
  • sending
    - 正在发送到运营商
  • sent
    - 已发送到运营商
  • delivered
    - 已送达接收方(需要配置StatusCallback)
  • undelivered
    - 送达失败
  • failed
    - 永久发送失败

6. Signature Validation (HMAC-SHA1)

6. 签名验证(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
Twilio使用HMAC-SHA1为Webhook签名:
  1. 拼接URL + 排序后的参数
  2. 使用Auth Token作为密钥生成HMAC-SHA1签名
  3. 对结果进行Base64编码
  4. X-Twilio-Signature
    头信息对比

7. A2P 10DLC Registration

7. A2P 10DLC注册

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
针对美国地区的消息发送,需要注册:
  1. 品牌 - 你的商业实体
  2. 营销活动 - 使用场景(客户服务、营销、双因素认证等)
  3. 电话号码 - 将号码与营销活动关联
审核周期: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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');

  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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');

  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-01
https://api.twilio.com/2010-04-01

Authentication

身份验证

Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)
Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN)

Environment Variables

环境变量

bash
undefined
bash
undefined

.env file

.env文件

TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token_here TWILIO_PHONE_NUMBER=+18005551234
undefined
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx TWILIO_AUTH_TOKEN=your_auth_token_here TWILIO_PHONE_NUMBER=+18005551234
undefined

Rate 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:
  1. Use Quick Reference #1 (Send Simple SMS)
  2. Set up environment variables
  3. Use Quick Reference #3 (Validate Phone Numbers)
  4. Test sending to your own phone number
  5. Set up Quick Reference #4 (Handle Incoming Messages with TwiML)
  6. 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. 使用快速参考#1(发送简单SMS)
  2. 配置环境变量
  3. 使用快速参考#3(验证电话号码)
  4. 测试向自己的号码发送消息
  5. 搭建快速参考#4(带TwiML的消息接收处理)
  6. 使用ngrok测试双向消息功能
需要掌握的核心概念:
  • E.164电话号码格式
  • 基础身份验证(Account SID + Auth Token)
  • 用于Webhook的TwiML XML响应
  • 消息状态生命周期
常见初学者错误:
  • 电话号码忘记添加
    +
    前缀
  • 未使用E.164格式
  • 硬编码凭证而非使用环境变量
  • Webhook端点未返回TwiML
  • 未验证Webhook签名

For Intermediate Users

中级用户指南

Focus Areas:
  1. Implement Quick Reference #5 or #6 (Signature Validation)
  2. Use Quick Reference #7 (Error Handling with Retry)
  3. Build conversation flows with state machines
  4. Implement idempotency (Production Pattern #5)
  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
重点关注:
  1. 实现快速参考#5或#6(签名验证)
  2. 使用快速参考#7(带重试的错误处理)
  3. 使用状态机构建对话流程
  4. 实现幂等性处理(生产实践模式#5)
  5. 处理StatusCallback Webhook以跟踪消息送达状态
需要掌握的核心概念:
  • HMAC-SHA1签名验证
  • TwiML高级功能
  • 消息分段与成本优化
  • 错误处理模式
  • 批量发送的速率限制

For Advanced Users

高级用户指南

Advanced Patterns:
  1. Build provider-agnostic handlers (Production Pattern #1)
  2. Implement timeout protection (Production Pattern #6)
  3. Design multi-provider architectures
  4. Optimize with fast mode vs compute mode (Production Pattern #3)
  5. Build IVR systems with Voice API
  6. 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. 构建多服务商兼容的处理器(生产实践模式#1)
  2. 实现超时保护(生产实践模式#6)
  3. 设计多服务商架构
  4. 使用快速模式vs计算模式优化(生产实践模式#3)
  5. 使用Voice API构建IVR系统
  6. 搭建全面的监控和告警系统
核心主题:
  • 多服务商兼容的Webhook架构
  • 基于数据库的幂等性
  • 结构化日志与监控
  • A2P 10DLC合规
  • 生产部署模式

Common Error Codes

常见错误码

Authentication Errors

身份验证错误

  • 20003
    - Authentication failed (check Account SID and Auth Token)
  • 20005
    - Account not active
  • 20003
    - 身份验证失败(检查Account SID和Auth Token)
  • 20005
    - 账号未激活

Validation Errors

验证错误

  • 21211
    - Invalid 'To' phone number
  • 21212
    - Invalid 'From' phone number
  • 21408
    - Permission to send to this number not enabled
  • 21610
    - Attempt to send to unsubscribed recipient
  • 21211
    - 无效的“To”电话号码
  • 21212
    - 无效的“From”电话号码
  • 21408
    - 无权限向该号码发送消息
  • 21610
    - 尝试向退订用户发送消息

Rate Limit Errors

速率限制错误

  • 20429
    - Too many requests (rate limited)
  • 20429
    - 请求过多(触发速率限制)

Message Errors

消息发送错误

  • 30001
    - Queue overflow (system overloaded)
  • 30003
    - Unreachable destination
  • 30004
    - Message blocked
  • 30005
    - Unknown destination
  • 30006
    - Landline or unreachable carrier
  • 30007
    - Message filtered (spam)
  • 30008
    - Unknown error
  • 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

资源链接

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示例