twilio-voice

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

twilio-voice

Twilio Voice

Purpose

用途

Enable OpenClaw to design, implement, and operate Twilio Programmable Voice in production:
  • Build inbound/outbound call flows using TwiML (
    <Dial>
    ,
    <Conference>
    ,
    <Gather>
    ,
    <Record>
    ,
    <Say>
    with Amazon Polly voices).
  • Implement IVR state machines with deterministic transitions, retries, and idempotency.
  • Operate call recording + transcription pipelines with retention controls and compliance guardrails.
  • Integrate SIP Trunking / SIP Interfaces, BYOC (Bring Your Own Carrier), and edge routing.
  • Run conferencing at scale (participant management, moderation, recording, events).
  • Diagnose and remediate common Twilio Voice failures (auth, invalid numbers, rate limits, carrier issues, webhook retries).
This skill is written for engineers shipping and maintaining production voice systems with strict reliability, observability, and security requirements.

帮助OpenClaw在生产环境中设计、实现和运维Twilio可编程语音服务:
  • 使用TwiML构建呼入/外呼呼叫流程(包含
    <Dial>
    <Conference>
    <Gather>
    <Record>
    、结合Amazon Polly语音的
    <Say>
    等标签)。
  • 实现具备确定性流转、重试机制和幂等性的IVR状态机。
  • 运维带有留存管控和合规防护的录音+转写流水线。
  • 集成SIP中继/SIP接口、BYOC(自有运营商接入)和边缘路由。
  • 规模化运行会议服务(参会者管理、 moderation、录音、事件处理)。
  • 诊断并修复Twilio Voice常见故障(认证失败、无效号码、速率限制、运营商问题、Webhook重试)。
本技能面向需要交付并维护具备严格可靠性、可观测性和安全性要求的生产级语音系统的工程师。

Prerequisites

前置条件

Accounts & Twilio Console setup

账号与Twilio控制台配置

  • Twilio account with:
    • Account SID (
      AC...
      )
    • Auth Token (treat as secret)
    • At least one Voice-capable phone number (E.164) or SIP domain
  • Configure a Voice webhook for your Twilio number:
    • Console → Phone Numbers → Manage → Active numbers → (number) → Voice configuration
    • Set A CALL COMES IN to your webhook URL (HTTPS) and method (POST recommended)
    • Set Status Callback for call progress events (optional but recommended)
  • 具备以下信息的Twilio账号:
    • Account SID(格式为
      AC...
    • Auth Token(需保密)
    • 至少一个支持语音功能的电话号码(E.164格式)或SIP域名
  • 为Twilio号码配置Voice Webhook:
    • 控制台 → 电话号码 → 管理 → 活跃号码 → (目标号码) → 语音配置
    • 将“来电时”设置为你的Webhook URL(HTTPS协议)和请求方法(推荐POST)
    • 为呼叫进度事件设置“状态回调”(可选但推荐)

Runtime versions (tested)

已测试的运行时版本

Pick one backend stack; examples cover Node and Python.
  • Node.js: 20.11.1 LTS (or 18.19.1 LTS)
  • Python: 3.11.8 (or 3.10.13)
  • Twilio helper libraries
    • Node:
      twilio@4.23.0
    • Python:
      twilio==9.0.5
  • ngrok (for local webhook dev): 3.14.2
  • Docker (optional): 25.0.3
  • OpenSSL: 3.0.x (Linux), 3.2.x (macOS via Homebrew)
选择一种后端技术栈;示例涵盖Node和Python。
  • Node.js:20.11.1 LTS(或18.19.1 LTS)
  • Python:3.11.8(或3.10.13)
  • Twilio辅助库
    • Node:
      twilio@4.23.0
    • Python:
      twilio==9.0.5
  • ngrok(用于本地Webhook开发):3.14.2
  • Docker(可选):25.0.3
  • OpenSSL:3.0.x(Linux),3.2.x(macOS通过Homebrew安装)

OS support

操作系统支持

  • Ubuntu 22.04 LTS (x86_64, arm64)
  • Fedora 39/40
  • macOS 14 Sonoma (Intel + Apple Silicon)
  • Ubuntu 22.04 LTS(x86_64、arm64)
  • Fedora 39/40
  • macOS 14 Sonoma(Intel + Apple Silicon)

Network & DNS

网络与DNS

  • Public HTTPS endpoint for Twilio webhooks (TLS 1.2+).
  • If using SIP:
    • Publicly reachable SIP infrastructure or Twilio SIP Domains.
    • Firewall rules for SIP signaling/media (or use Twilio Elastic SIP Trunking with secure signaling).
  • 供Twilio Webhook使用的公开HTTPS端点(需支持TLS 1.2+)。
  • 若使用SIP:
    • 可公开访问的SIP基础设施或Twilio SIP域名。
    • 为SIP信令/媒体配置防火墙规则(或使用带有安全信令的Twilio弹性SIP中继)。

Secrets management

密钥管理

  • Store
    TWILIO_ACCOUNT_SID
    and
    TWILIO_AUTH_TOKEN
    in a secret manager:
    • AWS Secrets Manager / GCP Secret Manager / Vault / 1Password CLI
  • Never commit credentials to git.
  • Rotate Auth Token on incident response or staff changes.

  • TWILIO_ACCOUNT_SID
    TWILIO_AUTH_TOKEN
    存储在密钥管理器中:
    • AWS Secrets Manager / GCP Secret Manager / Vault / 1Password CLI
  • 切勿将凭据提交至git仓库。
  • 在发生安全事件或人员变动时轮换Auth Token。

Core Concepts

核心概念

TwiML execution model

TwiML执行模型

  • Twilio requests your webhook when a call arrives (or when you initiate an outbound call with a TwiML URL).
  • Your server returns TwiML (XML) instructions.
  • Twilio executes TwiML sequentially; some verbs (e.g.,
    <Dial>
    ) block until completion.
  • TwiML is stateless; state must be stored externally (DB/Redis) and referenced via parameters.
  • 当呼叫接入(或你通过TwiML URL发起外呼)时,Twilio会请求你的Webhook。
  • 你的服务器返回TwiML(XML格式)指令。
  • Twilio会按顺序执行TwiML;部分动词(如
    <Dial>
    )会阻塞直至执行完成。
  • TwiML是无状态的;状态必须存储在外部(数据库/Redis)并通过参数引用。

Call legs, SIDs, and correlation

呼叫链路、SID与关联

  • CallSid
    identifies a call leg (inbound leg, outbound leg, child calls).
  • For
    <Dial>
    , Twilio often creates a child call for the dialed party; correlate via:
    • ParentCallSid
      (in status callbacks / call logs)
  • Use
    CallSid
    as the primary correlation key in logs and traces.
  • CallSid
    用于标识呼叫链路(呼入链路、外呼链路、子呼叫)。
  • 使用
    <Dial>
    时,Twilio通常会为被呼叫方创建一个子呼叫;可通过以下方式关联:
    • ParentCallSid
      (在状态回调/呼叫日志中)
  • 在日志和追踪中使用
    CallSid
    作为主要关联键。

Webhooks and retries

Webhook与重试

  • Twilio webhooks are HTTP requests to your infrastructure.
  • Twilio retries on certain failures (timeouts, 5xx). You must implement:
    • Idempotency (dedupe by
      CallSid
      + event type + timestamp)
    • Fast responses (< 2s typical target) and async work offloaded to queues
  • Twilio Webhook是发送至你的基础设施的HTTP请求。
  • Twilio会在特定故障时重试(超时、5xx错误)。你必须实现:
    • 幂等性(通过
      CallSid
      + 事件类型 + 时间戳去重)
    • 快速响应(典型目标为<2秒),并将异步工作卸载至队列

Status callbacks (events)

状态回调(事件)

  • Voice status callback events include:
    • initiated
      ,
      ringing
      ,
      answered
      ,
      completed
  • For conferences and recordings, additional callbacks exist.
  • Treat callbacks as at-least-once delivery.
  • 语音状态回调事件包括:
    • initiated
      ringing
      answered
      completed
  • 针对会议和录音,还有额外的回调事件。
  • 回调事件的交付机制为至少一次

IVR state machines

IVR状态机

  • Use
    <Gather>
    to collect DTMF or speech.
  • Model IVR as a state machine:
    • State stored server-side (Redis/DB)
    • Transition on input + timeout + retries
    • Always handle invalid input and no-input paths
  • 使用
    <Gather>
    收集DTMF或语音输入。
  • 将IVR建模为状态机:
    • 状态存储在服务器端(Redis/数据库)
    • 根据输入、超时和重试进行状态流转
    • 必须处理无效输入和无输入场景

Recording and transcription

录音与转写

  • Recording can be enabled on
    <Dial>
    ,
    <Conference>
    , or via
    <Record>
    .
  • Transcription can be:
    • Twilio’s transcription features (where available)
    • External transcription pipeline (recommended for control/quality)
  • Define retention and access controls; recordings are sensitive.
  • 可在
    <Dial>
    <Conference>
    或通过
    <Record>
    启用录音。
  • 转写方式包括:
    • Twilio自带的转写功能(视可用性而定)
    • 外部转写流水线(推荐用于自定义控制/品质要求)
  • 定义留存和访问控制;录音属于敏感数据。

SIP, BYOC, and edge

SIP、BYOC与边缘节点

  • SIP Domains: Twilio-hosted SIP endpoints for your clients.
  • Elastic SIP Trunking: connect your PBX/carrier to Twilio.
  • BYOC: bring your own carrier and use Twilio for apps/features.
  • Use Twilio Edge Locations to reduce latency (e.g.,
    ashburn
    ,
    dublin
    ,
    singapore
    ).

  • SIP域名:Twilio托管的SIP端点,供你的客户端使用。
  • 弹性SIP中继:将你的PBX/运营商连接至Twilio。
  • BYOC:接入自有运营商,同时使用Twilio的应用逻辑和功能。
  • 使用Twilio边缘节点降低延迟(例如
    ashburn
    dublin
    singapore
    )。

Installation & Setup

安装与配置

Official Python SDK — Voice

官方Python SDK — 语音服务

Repository: https://github.com/twilio/twilio-python
PyPI:
pip install twilio
· Supported: Python 3.7–3.13
python
from twilio.rest import Client
from twilio.twiml.voice_response import VoiceResponse

client = Client()
仓库地址https://github.com/twilio/twilio-python
PyPI安装
pip install twilio
· 支持版本:Python 3.7–3.13
python
from twilio.rest import Client
from twilio.twiml.voice_response import VoiceResponse

client = Client()

Outbound call

Outbound call

call = client.calls.create( url="https://demo.twilio.com/docs/voice.xml", to="+15558675309", from_="+15017250604" ) print(call.sid)
call = client.calls.create( url="https://demo.twilio.com/docs/voice.xml", to="+15558675309", from_="+15017250604" ) print(call.sid)

TwiML response (in webhook handler)

TwiML response (in webhook handler)

resp = VoiceResponse() resp.say("Hello from Twilio Python!") resp.record(transcribe=True, transcribe_callback="/transcription")

Source: [twilio/twilio-python — calls](https://github.com/twilio/twilio-python/blob/main/twilio/rest/api/v2010/account/call/__init__.py)
resp = VoiceResponse() resp.say("Hello from Twilio Python!") resp.record(transcribe=True, transcribe_callback="/transcription")

来源:[twilio/twilio-python — calls](https://github.com/twilio/twilio-python/blob/main/twilio/rest/api/v2010/account/call/__init__.py)

1) Install dependencies (Ubuntu 22.04)

1) 安装依赖(Ubuntu 22.04)

bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg jq
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v   # v20.11.1
npm -v    # 10.x
Python option:
bash
sudo apt-get update
sudo apt-get install -y python3.11 python3.11-venv python3-pip
python3.11 -V  # Python 3.11.8
ngrok:
bash
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
  | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
  | sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt-get update && sudo apt-get install -y ngrok
ngrok version  # 3.14.2
bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg jq
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v   # v20.11.1
npm -v    # 10.x
Python选项:
bash
sudo apt-get update
sudo apt-get install -y python3.11 python3.11-venv python3-pip
python3.11 -V  # Python 3.11.8
ngrok:
bash
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
  | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
  | sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt-get update && sudo apt-get install -y ngrok
ngrok version  # 3.14.2

2) Install dependencies (Fedora 39/40)

2) 安装依赖(Fedora 39/40)

bash
sudo dnf install -y nodejs20 jq python3.11 python3.11-pip
node -v
python3.11 -V
ngrok (manual):
bash
curl -L -o /tmp/ngrok.tgz https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz
sudo tar -C /usr/local/bin -xzf /tmp/ngrok.tgz ngrok
ngrok version
bash
sudo dnf install -y nodejs20 jq python3.11 python3.11-pip
node -v
python3.11 -V
ngrok(手动安装):
bash
curl -L -o /tmp/ngrok.tgz https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz
sudo tar -C /usr/local/bin -xzf /tmp/ngrok.tgz ngrok
ngrok version

3) Install dependencies (macOS 14, Intel + Apple Silicon)

3) 安装依赖(macOS 14,Intel + Apple Silicon)

Homebrew:
bash
brew update
brew install node@20 python@3.11 jq ngrok/ngrok/ngrok
node -v
python3.11 -V
ngrok version
Ensure PATH includes Homebrew Node:
bash
echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc  # Apple Silicon
echo 'export PATH="/usr/local/opt/node@20/bin:$PATH"' >> ~/.zshrc     # Intel
source ~/.zshrc
通过Homebrew安装:
bash
brew update
brew install node@20 python@3.11 jq ngrok/ngrok/ngrok
node -v
python3.11 -V
ngrok version
确保PATH包含Homebrew安装的Node:
bash
echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc  # Apple Silicon
echo 'export PATH="/usr/local/opt/node@20/bin:$PATH"' >> ~/.zshrc     # Intel
source ~/.zshrc

4) Create a minimal Voice webhook service (Node.js)

4) 创建最小化Voice Webhook服务(Node.js)

Project layout:
  • /srv/twilio-voice/
    (Linux) or
    ~/src/twilio-voice/
    (macOS)
  • Config at
    /etc/twilio/voice.env
    (Linux) or
    ./.env
    (local dev)
bash
mkdir -p ~/src/twilio-voice && cd ~/src/twilio-voice
npm init -y
npm install twilio@4.23.0 express@4.18.3 body-parser@1.20.2 pino@9.0.0
Create
server.js
:
javascript
const express = require("express");
const bodyParser = require("body-parser");
const twilio = require("twilio");
const pino = require("pino");

const log = pino({ level: process.env.LOG_LEVEL || "info" });

const app = express();

// Twilio sends application/x-www-form-urlencoded by default
app.use(bodyParser.urlencoded({ extended: false }));

// Optional: validate Twilio signature (recommended in production)
const validateTwilio = (req, res, next) => {
  const authToken = process.env.TWILIO_AUTH_TOKEN;
  const signature = req.header("X-Twilio-Signature");
  const url = `${process.env.PUBLIC_BASE_URL}${req.originalUrl}`;
  const isValid = twilio.validateRequest(authToken, signature, url, req.body);
  if (!isValid) return res.status(403).send("Invalid Twilio signature");
  next();
};

app.post("/voice/inbound", validateTwilio, (req, res) => {
  const vr = new twilio.twiml.VoiceResponse();

  // Example: simple IVR greeting + gather
  const gather = vr.gather({
    input: "dtmf",
    numDigits: 1,
    timeout: 5,
    action: "/voice/menu",
    method: "POST",
  });

  gather.say(
    { voice: "Polly.Joanna", language: "en-US" },
    "Press 1 for sales. Press 2 for support."
  );

  vr.say({ voice: "Polly.Joanna", language: "en-US" }, "No input received. Goodbye.");
  vr.hangup();

  res.type("text/xml").send(vr.toString());
});

app.post("/voice/menu", validateTwilio, (req, res) => {
  const digit = req.body.Digits;
  const vr = new twilio.twiml.VoiceResponse();

  if (digit === "1") {
    vr.dial({ callerId: process.env.TWILIO_CALLER_ID }, "+14155550100");
  } else if (digit === "2") {
    vr.dial({ callerId: process.env.TWILIO_CALLER_ID }, "+14155550101");
  } else {
    vr.say({ voice: "Polly.Joanna", language: "en-US" }, "Invalid choice.");
    vr.redirect({ method: "POST" }, "/voice/inbound");
  }

  res.type("text/xml").send(vr.toString());
});

app.get("/healthz", (req, res) => res.status(200).send("ok"));

const port = Number(process.env.PORT || 3000);
app.listen(port, () => log.info({ port }, "twilio-voice server listening"));
Run locally:
bash
export TWILIO_AUTH_TOKEN="your_auth_token"
export PUBLIC_BASE_URL="https://example.ngrok-free.app"
export TWILIO_CALLER_ID="+14155551234"
node server.js
Expose via ngrok:
bash
ngrok http 3000
项目结构:
  • Linux:
    /srv/twilio-voice/
    ;macOS:
    ~/src/twilio-voice/
  • 配置文件:Linux:
    /etc/twilio/voice.env
    ;本地开发:
    ./.env
bash
mkdir -p ~/src/twilio-voice && cd ~/src/twilio-voice
npm init -y
npm install twilio@4.23.0 express@4.18.3 body-parser@1.20.2 pino@9.0.0
创建
server.js
javascript
const express = require("express");
const bodyParser = require("body-parser");
const twilio = require("twilio");
const pino = require("pino");

const log = pino({ level: process.env.LOG_LEVEL || "info" });

const app = express();

// Twilio sends application/x-www-form-urlencoded by default
app.use(bodyParser.urlencoded({ extended: false }));

// Optional: validate Twilio signature (recommended in production)
const validateTwilio = (req, res, next) => {
  const authToken = process.env.TWILIO_AUTH_TOKEN;
  const signature = req.header("X-Twilio-Signature");
  const url = `${process.env.PUBLIC_BASE_URL}${req.originalUrl}`;
  const isValid = twilio.validateRequest(authToken, signature, url, req.body);
  if (!isValid) return res.status(403).send("Invalid Twilio signature");
  next();
};

app.post("/voice/inbound", validateTwilio, (req, res) => {
  const vr = new twilio.twiml.VoiceResponse();

  // Example: simple IVR greeting + gather
  const gather = vr.gather({
    input: "dtmf",
    numDigits: 1,
    timeout: 5,
    action: "/voice/menu",
    method: "POST",
  });

  gather.say(
    { voice: "Polly.Joanna", language: "en-US" },
    "Press 1 for sales. Press 2 for support."
  );

  vr.say({ voice: "Polly.Joanna", language: "en-US" }, "No input received. Goodbye.");
  vr.hangup();

  res.type("text/xml").send(vr.toString());
});

app.post("/voice/menu", validateTwilio, (req, res) => {
  const digit = req.body.Digits;
  const vr = new twilio.twiml.VoiceResponse();

  if (digit === "1") {
    vr.dial({ callerId: process.env.TWILIO_CALLER_ID }, "+14155550100");
  } else if (digit === "2") {
    vr.dial({ callerId: process.env.TWILIO_CALLER_ID }, "+14155550101");
  } else {
    vr.say({ voice: "Polly.Joanna", language: "en-US" }, "Invalid choice.");
    vr.redirect({ method: "POST" }, "/voice/inbound");
  }

  res.type("text/xml").send(vr.toString());
});

app.get("/healthz", (req, res) => res.status(200).send("ok"));

const port = Number(process.env.PORT || 3000);
app.listen(port, () => log.info({ port }, "twilio-voice server listening"));
本地运行:
bash
export TWILIO_AUTH_TOKEN="your_auth_token"
export PUBLIC_BASE_URL="https://example.ngrok-free.app"
export TWILIO_CALLER_ID="+14155551234"
node server.js
通过ngrok暴露服务:
bash
ngrok http 3000

copy the https URL into PUBLIC_BASE_URL and Twilio Console webhook

复制HTTPS URL到PUBLIC_BASE_URL和Twilio控制台的Webhook设置中

undefined
undefined

5) Python alternative (FastAPI)

5) Python替代方案(FastAPI)

bash
mkdir -p ~/src/twilio-voice-py && cd ~/src/twilio-voice-py
python3.11 -m venv .venv
source .venv/bin/activate
pip install "twilio==9.0.5" "fastapi==0.109.2" "uvicorn[standard]==0.27.1"
app.py
:
python
import os
from fastapi import FastAPI, Request, Response
from twilio.twiml.voice_response import VoiceResponse, Gather
from twilio.request_validator import RequestValidator

app = FastAPI()

def validate_twilio(request: Request, form: dict) -> None:
    auth_token = os.environ["TWILIO_AUTH_TOKEN"]
    validator = RequestValidator(auth_token)
    signature = request.headers.get("X-Twilio-Signature", "")
    url = os.environ["PUBLIC_BASE_URL"] + str(request.url.path)
    if not validator.validate(url, form, signature):
        raise PermissionError("Invalid Twilio signature")

@app.post("/voice/inbound")
async def inbound(request: Request):
    form = dict(await request.form())
    validate_twilio(request, form)

    vr = VoiceResponse()
    gather = Gather(input="dtmf", num_digits=1, timeout=5, action="/voice/menu", method="POST")
    gather.say("Press 1 for sales. Press 2 for support.", voice="Polly.Joanna", language="en-US")
    vr.append(gather)
    vr.say("No input received. Goodbye.", voice="Polly.Joanna", language="en-US")
    vr.hangup()
    return Response(content=str(vr), media_type="text/xml")

@app.post("/voice/menu")
async def menu(request: Request):
    form = dict(await request.form())
    validate_twilio(request, form)

    digit = form.get("Digits")
    vr = VoiceResponse()
    if digit == "1":
        vr.dial("+14155550100", caller_id=os.environ["TWILIO_CALLER_ID"])
    elif digit == "2":
        vr.dial("+14155550101", caller_id=os.environ["TWILIO_CALLER_ID"])
    else:
        vr.say("Invalid choice.", voice="Polly.Joanna", language="en-US")
        vr.redirect("/voice/inbound", method="POST")
    return Response(content=str(vr), media_type="text/xml")
Run:
bash
export TWILIO_AUTH_TOKEN="your_auth_token"
export PUBLIC_BASE_URL="https://example.ngrok-free.app"
export TWILIO_CALLER_ID="+14155551234"
uvicorn app:app --host 0.0.0.0 --port 3000

bash
mkdir -p ~/src/twilio-voice-py && cd ~/src/twilio-voice-py
python3.11 -m venv .venv
source .venv/bin/activate
pip install "twilio==9.0.5" "fastapi==0.109.2" "uvicorn[standard]==0.27.1"
app.py
python
import os
from fastapi import FastAPI, Request, Response
from twilio.twiml.voice_response import VoiceResponse, Gather
from twilio.request_validator import RequestValidator

app = FastAPI()

def validate_twilio(request: Request, form: dict) -> None:
    auth_token = os.environ["TWILIO_AUTH_TOKEN"]
    validator = RequestValidator(auth_token)
    signature = request.headers.get("X-Twilio-Signature", "")
    url = os.environ["PUBLIC_BASE_URL"] + str(request.url.path)
    if not validator.validate(url, form, signature):
        raise PermissionError("Invalid Twilio signature")

@app.post("/voice/inbound")
async def inbound(request: Request):
    form = dict(await request.form())
    validate_twilio(request, form)

    vr = VoiceResponse()
    gather = Gather(input="dtmf", num_digits=1, timeout=5, action="/voice/menu", method="POST")
    gather.say("Press 1 for sales. Press 2 for support.", voice="Polly.Joanna", language="en-US")
    vr.append(gather)
    vr.say("No input received. Goodbye.", voice="Polly.Joanna", language="en-US")
    vr.hangup()
    return Response(content=str(vr), media_type="text/xml")

@app.post("/voice/menu")
async def menu(request: Request):
    form = dict(await request.form())
    validate_twilio(request, form)

    digit = form.get("Digits")
    vr = VoiceResponse()
    if digit == "1":
        vr.dial("+14155550100", caller_id=os.environ["TWILIO_CALLER_ID"])
    elif digit == "2":
        vr.dial("+14155550101", caller_id=os.environ["TWILIO_CALLER_ID"])
    else:
        vr.say("Invalid choice.", voice="Polly.Joanna", language="en-US")
        vr.redirect("/voice/inbound", method="POST")
    return Response(content=str(vr), media_type="text/xml")
运行:
bash
export TWILIO_AUTH_TOKEN="your_auth_token"
export PUBLIC_BASE_URL="https://example.ngrok-free.app"
export TWILIO_CALLER_ID="+14155551234"
uvicorn app:app --host 0.0.0.0 --port 3000

Key Capabilities

核心能力

Inbound call handling (TwiML webhook)

呼入呼叫处理(TwiML Webhook)

  • Validate
    X-Twilio-Signature
    against the exact URL Twilio requested.
  • Parse
    application/x-www-form-urlencoded
    .
  • Return TwiML with correct
    Content-Type: text/xml
    .
  • Keep webhook latency low; do not block on DB migrations, third-party APIs, or long computations.
Recommended inbound webhook contract:
  • Endpoint:
    POST /voice/inbound
  • Must handle:
    • missing/invalid parameters
    • repeated requests (retries)
    • unexpected HTTP methods (return 405)
  • 根据Twilio请求的精确URL验证
    X-Twilio-Signature
  • 解析
    application/x-www-form-urlencoded
    格式数据。
  • 返回带有正确
    Content-Type: text/xml
    的TwiML。
  • 降低Webhook延迟;不要在Webhook中阻塞数据库迁移、第三方API调用或长时间计算。
推荐的呼入Webhook约定:
  • 端点:
    POST /voice/inbound
  • 必须处理:
    • 缺失/无效参数
    • 重复请求(重试)
    • 意外的HTTP方法(返回405)

Outbound calling (REST API + TwiML URL)

外呼呼叫(REST API + TwiML URL)

  • Create outbound calls via REST:
    • Provide
      to
      ,
      from
      , and either
      url
      (TwiML URL) or
      twiml
      inline.
  • Use status callbacks for lifecycle events.
  • Use
    machineDetection
    only when you can tolerate extra latency and false positives.
  • 通过REST API创建外呼:
    • 提供
      to
      from
      ,以及
      url
      (TwiML URL)或内联
      twiml
  • 使用状态回调监听生命周期事件。
  • 仅当你能容忍额外延迟和误判时,才使用
    machineDetection

IVR with
<Gather>
(DTMF + speech)

基于
<Gather>
的IVR(DTMF + 语音)

  • DTMF is deterministic; speech requires tuning and fallback.
  • Always implement:
    • timeout
      path (no input)
    • invalid input path
    • max retries
  • For speech:
    • specify
      speechTimeout
      ,
      language
      , and consider barge-in behavior.
  • DTMF是确定性的;语音输入需要调优和降级方案。
  • 必须实现:
    • timeout
      场景(无输入)
    • 无效输入场景
    • 最大重试次数
  • 针对语音输入:
    • 指定
      speechTimeout
      language
      ,并考虑抢话行为。

Conferencing (
<Conference>
)

会议服务(
<Conference>

  • Use conferences for:
    • agent + customer + supervisor
    • warm transfers
    • multi-party bridges
  • Control:
    • startConferenceOnEnter
    • endConferenceOnExit
    • beep
    • record
      and recording callbacks
  • Track participants via Conference/Participant resources.
  • 会议服务适用于:
    • 坐席+客户+主管三方通话
    • 暖转移
    • 多方通话桥接
  • 控制选项:
    • startConferenceOnEnter
    • endConferenceOnExit
    • beep
    • record
      和录音回调
  • 通过Conference/Participant资源追踪参会者。

Call recording

呼叫录音

  • Options:
    • <Dial record="record-from-answer">
    • <Conference record="record-from-start">
    • <Record>
      verb for voicemail-like flows
  • Use recording status callbacks to ingest metadata and enforce retention.
  • Encrypt at rest in your storage if you export recordings.
  • 启用方式:
    • <Dial record="record-from-answer">
    • <Conference record="record-from-start">
    • <Record>
      动词用于类似语音信箱的流程
  • 使用录音状态回调获取元数据并执行留存规则。
  • 若导出录音,需在存储时加密静态数据。

Transcription

语音转写

  • Prefer external transcription for:
    • model choice
    • redaction
    • language detection
    • diarization
  • Pipeline:
    1. recording callback → enqueue job
    2. fetch recording media (authenticated)
    3. transcribe
    4. store transcript + link to
      CallSid
      /
      RecordingSid
  • 推荐使用外部转写服务的场景:
    • 自定义模型选择
    • 内容脱敏
    • 语言检测
    • 说话人分离
  • 流水线流程:
    1. 录音回调 → 加入任务队列
    2. 获取录音媒体(需认证)
    3. 执行转写
    4. 存储转写结果并关联
      CallSid
      /
      RecordingSid

SIP trunking / SIP domains / BYOC

SIP中继 / SIP域名 / BYOC

  • SIP Domains for client registration and inbound SIP to Twilio apps.
  • Elastic SIP Trunking for PBX connectivity.
  • BYOC for carrier control; Twilio still provides app logic and features.
  • SIP域名:供客户端注册,以及SIP呼入到Twilio应用的端点。
  • 弹性SIP中继:用于PBX与Twilio的连接。
  • BYOC:接入自有运营商,同时使用Twilio的应用逻辑和功能。

Voice SDK (browser/iOS/Android)

Voice SDK(浏览器/iOS/Android)

  • Use Access Tokens with Voice grants.
  • Token TTL: short (5–15 minutes) with refresh.
  • Enforce device registration and push notifications (mobile).

  • 使用带有Voice授权的访问令牌。
  • 令牌TTL:较短(5–15分钟),并支持刷新。
  • 强制设备注册和推送通知(移动端)。

Command Reference

命令参考

This section focuses on Twilio CLI and common operational commands. Twilio CLI is optional; production systems should use REST APIs and IaC where possible.
本节聚焦Twilio CLI和常见运维命令。Twilio CLI为可选工具;生产系统应尽可能使用REST API和基础设施即代码(IaC)。

Twilio CLI installation

Twilio CLI安装

macOS (Homebrew)

macOS(Homebrew)

bash
brew install twilio/brew/twilio
twilio --version
bash
brew install twilio/brew/twilio
twilio --version

Ubuntu/Debian (npm global)

Ubuntu/Debian(npm全局安装)

bash
sudo npm install -g twilio-cli@5.20.0
twilio --version
bash
sudo npm install -g twilio-cli@5.20.0
twilio --version

Fedora (npm global)

Fedora(npm全局安装)

bash
sudo npm install -g twilio-cli@5.20.0
twilio --version
bash
sudo npm install -g twilio-cli@5.20.0
twilio --version

Authenticate Twilio CLI

Twilio CLI认证

bash
twilio login
bash
twilio login

prompts for Account SID and Auth Token

提示输入Account SID和Auth Token


Non-interactive (CI) via env:

```bash
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

非交互式(CI环境)通过环境变量:

```bash
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Voice: list calls

语音:列出呼叫记录

bash
twilio api:core:calls:list \
  --limit 50 \
  --properties sid,from,to,status,startDate,endDate,duration,price,priceUnit
Relevant flags:
  • --limit <n>
    : max records
  • --properties <csv>
    : select fields (reduces noise)
bash
twilio api:core:calls:list \
  --limit 50 \
  --properties sid,from,to,status,startDate,endDate,duration,price,priceUnit
相关参数:
  • --limit <n>
    :最大记录数
  • --properties <csv>
    :选择字段(减少冗余信息)

Voice: fetch a call

语音:获取单个呼叫详情

bash
twilio api:core:calls:fetch --sid YOUR_CA_SID
Flags:
  • --sid <CA...>
    : Call SID (required)
bash
twilio api:core:calls:fetch --sid YOUR_CA_SID
参数:
  • --sid <CA...>
    :呼叫SID(必填)

Voice: create an outbound call

语音:创建外呼

bash
twilio api:core:calls:create \
  --from +14155551234 \
  --to +14155559876 \
  --url https://voice.example.com/twiml/outbound \
  --status-callback https://voice.example.com/webhooks/voice/status \
  --status-callback-event initiated ringing answered completed \
  --status-callback-method POST \
  --timeout 20
Relevant flags (core):
  • --from <E.164|client:...|sip:...>
    : caller ID / identity
  • --to <E.164|client:...|sip:...>
    : destination
  • --url <https://...>
    : TwiML URL (mutually exclusive with
    --twiml
    )
  • --twiml <xml>
    : inline TwiML
  • --method <GET|POST>
    : method for
    url
    fetch (default POST recommended)
  • --status-callback <url>
  • --status-callback-event <events...>
  • --status-callback-method <GET|POST>
  • --timeout <seconds>
    : ring timeout
  • --machine-detection <Enable|DetectMessageEnd|...>
    : AMD (adds latency)
  • --record
    : enable recording (where supported)
  • --recording-status-callback <url>
  • --recording-status-callback-method <GET|POST>
bash
twilio api:core:calls:create \
  --from +14155551234 \
  --to +14155559876 \
  --url https://voice.example.com/twiml/outbound \
  --status-callback https://voice.example.com/webhooks/voice/status \
  --status-callback-event initiated ringing answered completed \
  --status-callback-method POST \
  --timeout 20
核心相关参数:
  • --from <E.164|client:...|sip:...>
    :主叫ID / 身份标识
  • --to <E.164|client:...|sip:...>
    :被叫方
  • --url <https://...>
    :TwiML URL(与
    --twiml
    互斥)
  • --twiml <xml>
    :内联TwiML
  • --method <GET|POST>
    :获取
    url
    的请求方法(推荐默认POST)
  • --status-callback <url>
  • --status-callback-event <events...>
  • --status-callback-method <GET|POST>
  • --timeout <seconds>
    :振铃超时时间
  • --machine-detection <Enable|DetectMessageEnd|...>
    :自动应答检测(会增加延迟)
  • --record
    :启用录音(视支持情况)
  • --recording-status-callback <url>
  • --recording-status-callback-method <GET|POST>

Voice: list recordings

语音:列出录音记录

bash
twilio api:core:recordings:list --limit 20 \
  --properties sid,callSid,dateCreated,duration,price,priceUnit,status
bash
twilio api:core:recordings:list --limit 20 \
  --properties sid,callSid,dateCreated,duration,price,priceUnit,status

Voice: fetch recording metadata

语音:获取录音元数据

bash
twilio api:core:recordings:fetch --sid RE3f3b1b2c0d0f4a5b6c7d8e9f0a1b2c3
bash
twilio api:core:recordings:fetch --sid RE3f3b1b2c0d0f4a5b6c7d8e9f0a1b2c3

Voice: download recording media (authenticated)

语音:下载录音媒体(需认证)

Twilio CLI does not always provide direct media download helpers; use curl with basic auth:
bash
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
curl -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
  -L "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Recordings/RE3f3b1b2c0d0f4a5b6c7d8e9f0a1b2c3.wav" \
  -o recording.wav
file recording.wav
Twilio CLI并不总是提供直接的媒体下载工具;可使用curl结合基础认证:
bash
export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
curl -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
  -L "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Recordings/RE3f3b1b2c0d0f4a5b6c7d8e9f0a1b2c3.wav" \
  -o recording.wav
file recording.wav

Voice: conferences list/fetch

语音:会议列表/详情

bash
twilio api:core:conferences:list --status in-progress --limit 20
twilio api:core:conferences:fetch --sid YOUR_CF_SID
bash
twilio api:core:conferences:list --status in-progress --limit 20
twilio api:core:conferences:fetch --sid YOUR_CF_SID

Voice: conference participants list

语音:会议参会者列表

bash
twilio api:core:conferences:participants:list \
  --conference-sid YOUR_CF_SID \
  --limit 50
bash
twilio api:core:conferences:participants:list \
  --conference-sid YOUR_CF_SID \
  --limit 50

Voice: update participant (mute/kick)

语音:更新参会者状态(静音/移除)

bash
twilio api:core:conferences:participants:update \
  --conference-sid YOUR_CF_SID \
  --sid CAbcdef0123456789abcdef0123456789 \
  --muted true
Flags:
  • --muted <true|false>
  • --hold <true|false>
    (where supported)
  • --hold-url <url>
    (music/announcements while on hold)
bash
twilio api:core:conferences:participants:update \
  --conference-sid YOUR_CF_SID \
  --sid CAbcdef0123456789abcdef0123456789 \
  --muted true
参数:
  • --muted <true|false>
  • --hold <true|false>
    (视支持情况)
  • --hold-url <url>
    (保持时的音乐/通知)

Twilio CLI: API request (generic)

Twilio CLI:通用API请求

Use for endpoints not wrapped by CLI:
bash
twilio api:core:accounts:fetch
twilio api:request --method GET \
  --uri "/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json?PageSize=20"
Flags:
  • --method <GET|POST|PUT|DELETE>
  • --uri <path>
    : relative to
    https://api.twilio.com
  • --data <k=v>
    : form fields (repeatable)

用于CLI未封装的端点:
bash
twilio api:core:accounts:fetch
twilio api:request --method GET \
  --uri "/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json?PageSize=20"
参数:
  • --method <GET|POST|PUT|DELETE>
  • --uri <path>
    :相对
    https://api.twilio.com
    的路径
  • --data <k=v>
    :表单字段(可重复)

Configuration Reference

配置参考

Environment variables (Linux production)

环境变量(Linux生产环境)

File:
/etc/twilio/voice.env
bash
undefined
文件路径:
/etc/twilio/voice.env
bash
undefined

Twilio auth

Twilio认证

TWILIO_ACCOUNT_SID=AC2f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5 TWILIO_AUTH_TOKEN=9f8e7d6c5b4a3a2b1c0d9e8f7a6b5c4d
TWILIO_ACCOUNT_SID=AC2f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5 TWILIO_AUTH_TOKEN=9f8e7d6c5b4a3a2b1c0d9e8f7a6b5c4d

Public base URL used for signature validation

用于签名验证的公开基础URL

Caller ID must be a Twilio number or verified caller ID

主叫ID必须是Twilio号码或已验证的主叫ID

TWILIO_CALLER_ID=+14155551234
TWILIO_CALLER_ID=+14155551234

Webhook behavior

Webhook行为

LOG_LEVEL=info PORT=3000
LOG_LEVEL=info PORT=3000

Recording/transcription pipeline

录音/转写流水线

RECORDINGS_BUCKET=s3://prod-voice-recordings-us-east-1 TRANSCRIPTION_PROVIDER=deepgram TRANSCRIPTION_WEBHOOK_SECRET=whsec_6b1f0b2a9c3d4e5f

Systemd unit (Ubuntu/Fedora):

File: `/etc/systemd/system/twilio-voice.service`

```ini
[Unit]
Description=Twilio Voice Webhook Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=twilio
Group=twilio
EnvironmentFile=/etc/twilio/voice.env
WorkingDirectory=/srv/twilio-voice
ExecStart=/usr/bin/node /srv/twilio-voice/server.js
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/srv/twilio-voice
AmbientCapabilities=
CapabilityBoundingSet=
LockPersonality=true
MemoryDenyWriteExecute=true

[Install]
WantedBy=multi-user.target
Enable:
bash
sudo useradd --system --home /srv/twilio-voice --shell /usr/sbin/nologin twilio
sudo mkdir -p /srv/twilio-voice
sudo chown -R twilio:twilio /srv/twilio-voice
sudo systemctl daemon-reload
sudo systemctl enable --now twilio-voice
sudo systemctl status twilio-voice --no-pager
RECORDINGS_BUCKET=s3://prod-voice-recordings-us-east-1 TRANSCRIPTION_PROVIDER=deepgram TRANSCRIPTION_WEBHOOK_SECRET=whsec_6b1f0b2a9c3d4e5f

Systemd单元配置(Ubuntu/Fedora):

文件路径:`/etc/systemd/system/twilio-voice.service`

```ini
[Unit]
Description=Twilio Voice Webhook Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=twilio
Group=twilio
EnvironmentFile=/etc/twilio/voice.env
WorkingDirectory=/srv/twilio-voice
ExecStart=/usr/bin/node /srv/twilio-voice/server.js
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/srv/twilio-voice
AmbientCapabilities=
CapabilityBoundingSet=
LockPersonality=true
MemoryDenyWriteExecute=true

[Install]
WantedBy=multi-user.target
启用服务:
bash
sudo useradd --system --home /srv/twilio-voice --shell /usr/sbin/nologin twilio
sudo mkdir -p /srv/twilio-voice
sudo chown -R twilio:twilio /srv/twilio-voice
sudo systemctl daemon-reload
sudo systemctl enable --now twilio-voice
sudo systemctl status twilio-voice --no-pager

NGINX reverse proxy (TLS termination)

NGINX反向代理(TLS终止)

File:
/etc/nginx/sites-available/voice.prod.example.com
nginx
server {
  listen 443 ssl http2;
  server_name voice.prod.example.com;

  ssl_certificate     /etc/letsencrypt/live/voice.prod.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/voice.prod.example.com/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;

  # Twilio webhooks are small; keep limits tight
  client_max_body_size 64k;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_read_timeout 10s;
  }

  location = /healthz {
    proxy_pass http://127.0.0.1:3000/healthz;
  }
}
文件路径:
/etc/nginx/sites-available/voice.prod.example.com
nginx
server {
  listen 443 ssl http2;
  server_name voice.prod.example.com;

  ssl_certificate     /etc/letsencrypt/live/voice.prod.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/voice.prod.example.com/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;

  # Twilio Webhook数据量小;限制配置需严格
  client_max_body_size 64k;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_read_timeout 10s;
  }

  location = /healthz {
    proxy_pass http://127.0.0.1:3000/healthz;
  }
}

TwiML examples (stored templates)

TwiML示例(存储的模板)

If you store TwiML templates in-repo, keep them versioned:
Path:
/srv/twilio-voice/twiml/outbound.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Joanna" language="en-US">Connecting your call.</Say>
  <Dial callerId="+14155551234" record="record-from-answer" recordingStatusCallback="https://voice.prod.example.com/webhooks/recording">
    <Number>+14155559876</Number>
  </Dial>
</Response>

若将TwiML模板存储在仓库中,需进行版本控制:
路径:
/srv/twilio-voice/twiml/outbound.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Joanna" language="en-US">Connecting your call.</Say>
  <Dial callerId="+14155551234" record="record-from-answer" recordingStatusCallback="https://voice.prod.example.com/webhooks/recording">
    <Number>+14155559876</Number>
  </Dial>
</Response>

Integration Patterns

集成模式

Compose with Twilio Messaging (cluster requirement alignment)

与Twilio消息服务集成(集群需求对齐)

Common pattern: voice call fails → send SMS fallback with opt-out handling.
Flow:
  1. Attempt outbound call
  2. If status callback indicates
    completed
    with
    CallStatus=busy|no-answer|failed
  3. Send SMS: “We tried to reach you…” via Messaging Service
  4. Respect STOP/START keywords and compliance
Pseudo-pipeline:
mermaid
flowchart LR
  A[Create Call] --> B[Status Callback]
  B -->|answered| C[Normal completion]
  B -->|busy/no-answer/failed| D[Send SMS fallback]
  D --> E[Message Status Webhook]
常见模式:语音呼叫失败 → 发送SMS降级通知,并处理退订逻辑。
流程:
  1. 尝试发起外呼
  2. 若状态回调显示
    completed
    CallStatus=busy|no-answer|failed
  3. 通过消息服务发送SMS:“我们尝试联系您…”
  4. 遵守STOP/START关键字和合规要求
伪流水线:
mermaid
flowchart LR
  A[Create Call] --> B[Status Callback]
  B -->|answered| C[Normal completion]
  B -->|busy/no-answer/failed| D[Send SMS fallback]
  D --> E[Message Status Webhook]

Compose with Verify (2FA via voice)

与Verify集成(语音OTP)

  • Use Verify V2 for voice OTP when SMS is blocked.
  • Voice channel can be more expensive; rate limit and fraud guard.
  • 当SMS被拦截时,使用Verify V2发送语音OTP。
  • 语音渠道成本更高;需进行速率限制和欺诈防护。

Compose with Studio

与Studio集成

  • Use Studio for rapid iteration of IVR logic, but keep critical flows in code for versioning.
  • Trigger Studio Flow via REST Trigger API for certain branches (e.g., after-hours routing).
  • 使用Studio快速迭代IVR逻辑,但关键流程需保留在代码中以支持版本控制。
  • 通过REST触发API调用Studio Flow处理特定分支(例如非工作时间路由)。

Event-driven recording/transcription pipeline

事件驱动的录音/转写流水线

  • Recording status callback → enqueue job (SQS/PubSub/Kafka)
  • Worker downloads recording media → transcribes → stores transcript
  • Attach transcript to CRM ticket
Example (AWS SQS + worker):
  • Webhook service publishes message
    {RecordingSid, CallSid, RecordingUrl, Timestamp}
  • Worker uses Twilio REST to fetch recording metadata and media
  • 录音状态回调 → 加入任务队列(SQS/PubSub/Kafka)
  • 工作节点下载录音媒体 → 执行转写 → 存储转写结果
  • 将转写结果关联至CRM工单
示例(AWS SQS + 工作节点):
  • Webhook服务发布消息
    {RecordingSid, CallSid, RecordingUrl, Timestamp}
  • 工作节点使用Twilio REST API获取录音元数据和媒体

Observability integration

可观测性集成

  • Emit structured logs with
    CallSid
    ,
    ParentCallSid
    ,
    From
    ,
    To
    ,
    Direction
    .
  • Metrics:
    • webhook latency p50/p95
    • call answer rate
    • gather completion rate
    • conference join failures
  • Tracing:
    • propagate
      CallSid
      as trace attribute

  • 输出包含
    CallSid
    ParentCallSid
    From
    To
    Direction
    的结构化日志。
  • 指标:
    • Webhook延迟p50/p95
    • 呼叫接通率
    • Gather完成率
    • 会议加入失败率
  • 追踪:
    • CallSid
      作为追踪属性传递

Error Handling & Troubleshooting

错误处理与故障排查

Handle errors at three layers: webhook HTTP, Twilio REST API, and carrier/SIP.
在三个层级处理错误:Webhook HTTP错误、Twilio REST API错误、运营商/SIP错误。

1)
Twilio could not find a valid URL for the Voice request

1)
Twilio could not find a valid URL for the Voice request

Symptom (Console debugger):
  • “Twilio could not find a valid URL for the Voice request. Please check your TwiML App settings.”
Root cause:
  • Phone number Voice webhook not configured, or points to invalid URL.
Fix:
  • Console → Phone Numbers → (number) → Voice configuration → set webhook URL.
  • Ensure HTTPS and publicly reachable.

症状(控制台调试器):
  • “Twilio could not find a valid URL for the Voice request. Please check your TwiML App settings.”
根本原因:
  • 电话号码的Voice Webhook未配置,或指向无效URL。
修复方案:
  • 控制台 → 电话号码 → (目标号码) → 语音配置 → 设置Webhook URL。
  • 确保URL为HTTPS且可公开访问。

2)
11200 - HTTP retrieval failure

2)
11200 - HTTP retrieval failure

Symptom:
  • Twilio Debugger shows:
    • Error - 11200
    • “HTTP retrieval failure”
Root cause:
  • Your webhook endpoint timed out, returned 5xx, TLS handshake failed, or DNS issues.
Fix:
  • Ensure endpoint responds within ~2–5 seconds.
  • Check TLS cert validity and chain.
  • Verify firewall allows inbound from Twilio (don’t IP allowlist unless you maintain Twilio IP ranges).
  • Add
    /healthz
    and monitor.

症状:
  • Twilio调试器显示:
    • Error - 11200
    • “HTTP retrieval failure”
根本原因:
  • 你的Webhook端点超时、返回5xx错误、TLS握手失败或DNS问题。
修复方案:
  • 确保端点在约2–5秒内响应。
  • 检查TLS证书有效性和证书链。
  • 验证防火墙允许Twilio的入站请求(除非你维护Twilio IP范围列表,否则不要使用IP白名单)。
  • 添加
    /healthz
    端点并监控。

3)
12300 - Invalid Content-Type

3)
12300 - Invalid Content-Type

Symptom:
  • Twilio Debugger:
    • Error - 12300
    • “Invalid Content-Type”
Root cause:
  • Webhook returned TwiML but with wrong
    Content-Type
    (e.g.,
    application/json
    ).
Fix:
  • Set
    Content-Type: text/xml
    .
  • Ensure response body is valid XML.

症状:
  • Twilio调试器:
    • Error - 12300
    • “Invalid Content-Type”
根本原因:
  • Webhook返回TwiML但使用了错误的
    Content-Type
    (例如
    application/json
    )。
修复方案:
  • 设置
    Content-Type: text/xml
  • 确保响应体为有效的XML。

4)
12100 - Document parse failure

4)
12100 - Document parse failure

Symptom:
  • Twilio Debugger:
    • Error - 12100
    • “Document parse failure”
Root cause:
  • Malformed XML (unescaped characters, invalid nesting).
Fix:
  • Use helper library TwiML builders.
  • Validate generated XML; ensure
    &
    is escaped as
    &amp;
    .

症状:
  • Twilio调试器:
    • Error - 12100
    • “Document parse failure”
根本原因:
  • XML格式错误(未转义字符、无效嵌套)。
修复方案:
  • 使用辅助库的TwiML构建器。
  • 验证生成的XML;确保
    &
    转义为
    &amp;

5)
21211 - The 'To' number +... is not a valid phone number

5)
21211 - The 'To' number +... is not a valid phone number

Symptom (REST API response):
json
{
  "code": 21211,
  "message": "The 'To' number +1415555 is not a valid phone number.",
  "status": 400
}
Root cause:
  • Non-E.164 formatting, missing country code, invalid characters.
Fix:
  • Normalize to E.164 (
    +14155551212
    ).
  • Validate with libphonenumber before calling Twilio.
  • For SIP, use
    sip:user@domain
    .

症状(REST API响应):
json
{
  "code": 21211,
  "message": "The 'To' number +1415555 is not a valid phone number.",
  "status": 400
}
根本原因:
  • 非E.164格式、缺少国家代码、包含无效字符。
修复方案:
  • 标准化为E.164格式(例如
    +14155551212
    )。
  • 在调用Twilio前使用libphonenumber验证号码。
  • 对于SIP,使用
    sip:user@domain
    格式。

6)
20003 - Authenticate

6)
20003 - Authenticate

Symptom:
  • REST API returns:
    • HTTP 401
    • code: 20003
    • message: “Authenticate”
Root cause:
  • Wrong Account SID/Auth Token, rotated token, or using test creds against live endpoints.
Fix:
  • Verify secrets in runtime environment.
  • Rotate and redeploy.
  • Ensure you’re using correct subaccount credentials if applicable.

症状:
  • REST API返回:
    • HTTP 401
    • code: 20003
    • message: “Authenticate”
根本原因:
  • 错误的Account SID/Auth Token、令牌已轮换,或使用测试凭据访问生产端点。
修复方案:
  • 验证运行时环境中的密钥。
  • 轮换密钥并重新部署。
  • 若使用子账号,确保使用正确的子账号凭据。

7)
20429 - Too Many Requests

7)
20429 - Too Many Requests

Symptom:
  • REST API returns:
    • HTTP 429
    • code: 20429
    • message: “Too Many Requests”
Root cause:
  • Rate limit exceeded (bursting call creates, conference ops, recording fetches).
Fix:
  • Implement exponential backoff with jitter.
  • Queue outbound call creation; cap concurrency.
  • Cache call/recording metadata to reduce repeated fetches.

症状:
  • REST API返回:
    • HTTP 429
    • code: 20429
    • message: “Too Many Requests”
根本原因:
  • 超出速率限制(批量创建呼叫、会议操作、获取录音)。
修复方案:
  • 实现带抖动的指数退避算法。
  • 将外呼创建加入队列;限制并发数。
  • 缓存呼叫/录音元数据以减少重复获取。

8)
30003 - Unreachable destination handset

8)
30003 - Unreachable destination handset

Symptom:
  • Call ends quickly; status callback indicates failure.
  • Twilio error code
    30003
    .
Root cause:
  • Carrier unreachable, invalid routing, handset off-network, or blocked by carrier.
Fix:
  • Retry with backoff only if business-appropriate.
  • Consider alternate routes (BYOC) for critical traffic.
  • Confirm destination is voice-capable and not blocked.

症状:
  • 呼叫快速结束;状态回调显示失败。
  • Twilio错误码
    30003
根本原因:
  • 运营商不可达、路由无效、终端离线或被运营商拦截。
修复方案:
  • 仅当业务允许时,使用退避策略重试。
  • 对于关键流量,考虑使用替代路由(BYOC)。
  • 确认被叫方支持语音功能且未被拦截。

9)
403 Invalid Twilio signature
(your app)

9)
403 Invalid Twilio signature
(你的应用)

Symptom:
  • Your service returns 403 with message “Invalid Twilio signature”.
Root cause:
  • Signature validation uses wrong URL (missing scheme/host, wrong path, ngrok URL changed).
  • Proxy not forwarding correct
    Host
    /
    X-Forwarded-Proto
    .
Fix:
  • Compute validation URL using externally visible URL.
  • Ensure
    PUBLIC_BASE_URL
    matches Twilio-configured webhook base.
  • In NGINX, forward
    Host
    and
    X-Forwarded-Proto
    .

症状:
  • 你的服务返回403并显示消息“Invalid Twilio signature”。
根本原因:
  • 签名验证使用了错误的URL(缺少协议/主机、错误路径、ngrok URL已变更)。
  • 代理未转发正确的
    Host
    /
    X-Forwarded-Proto
修复方案:
  • 使用外部可见的URL计算验证地址。
  • 确保
    PUBLIC_BASE_URL
    与Twilio配置的Webhook基础URL一致。
  • 在NGINX中转发
    Host
    X-Forwarded-Proto

10) SIP:
488 Not Acceptable Here
/ no audio

10) SIP:
488 Not Acceptable Here
/ 无音频

Symptom:
  • SIP call connects but no audio, or SIP rejects with 488.
Root cause:
  • Codec mismatch, SRTP mismatch, NAT/firewall blocking RTP.
Fix:
  • Ensure common codecs (PCMU/PCMA/Opus where supported).
  • If using SRTP, align policies.
  • Open RTP ports or use Twilio-managed media; verify symmetric RTP.

症状:
  • SIP呼叫接通但无音频,或SIP返回488错误。
根本原因:
  • 编解码器不匹配、SRTP不匹配、NAT/防火墙拦截RTP。
修复方案:
  • 确保使用通用编解码器(PCMU/PCMA/Opus,视支持情况)。
  • 若使用SRTP,对齐策略配置。
  • 开放RTP端口或使用Twilio托管媒体;验证对称RTP。

Security Hardening

安全加固

Webhook authenticity

Webhook真实性

  • Validate
    X-Twilio-Signature
    on every webhook.
  • Ensure you validate against the exact URL Twilio requested (scheme/host/path).
  • Reject requests missing signature with 403.
  • 对每个Webhook验证
    X-Twilio-Signature
  • 确保针对Twilio请求的精确URL(协议/主机/路径)进行验证。
  • 拒绝缺少签名的请求并返回403。

TLS and HTTP

TLS与HTTP

  • Enforce TLS 1.2+ (prefer TLS 1.3).
  • Disable weak ciphers (use modern NGINX defaults).
  • Set strict timeouts:
    • upstream read timeout 10s
    • request body size 64k
  • 强制使用TLS 1.2+(优先TLS 1.3)。
  • 禁用弱密码套件(使用NGINX现代默认配置)。
  • 设置严格超时:
    • 上游读取超时10秒
    • 请求体大小限制64k

Secrets

密钥管理

  • Store Auth Token in secret manager; do not log it.
  • Rotate Auth Token periodically (quarterly) and on incident.
  • Use subaccounts to isolate environments (dev/stage/prod).
  • 将Auth Token存储在密钥管理器中;切勿记录日志。
  • 定期(每季度)并在发生安全事件时轮换Auth Token。
  • 使用子账号隔离环境(开发/测试/生产)。

Least privilege (system)

最小权限(系统层面)

  • Run webhook service as non-root user.
  • Systemd hardening (example unit already includes):
    • NoNewPrivileges=true
    • ProtectSystem=strict
    • ProtectHome=true
    • PrivateTmp=true
  • Align with CIS Benchmarks:
    • CIS Ubuntu Linux 22.04 LTS Benchmark (sections on service hardening, file permissions)
    • CIS NGINX Benchmark (TLS configuration, headers, logging)
  • 以非root用户运行Webhook服务。
  • Systemd加固(示例单元已包含):
    • NoNewPrivileges=true
    • ProtectSystem=strict
    • ProtectHome=true
    • PrivateTmp=true
  • 对齐CIS基准:
    • CIS Ubuntu Linux 22.04 LTS基准(服务加固、文件权限相关章节)
    • CIS NGINX基准(TLS配置、请求头、日志相关章节)

Data protection (recordings/transcripts)

数据保护(录音/转写结果)

  • Treat recordings/transcripts as sensitive:
    • encrypt at rest (KMS)
    • restrict access via IAM
    • audit access logs
  • Define retention:
    • e.g., delete recordings after 30 days unless legal hold
  • If exporting recordings, use signed URLs with short TTL.
  • 将录音/转写结果视为敏感数据:
    • 静态加密(KMS)
    • 通过IAM限制访问
    • 审计访问日志
  • 定义留存规则:
    • 例如,除非有法律保留要求,否则30天后删除录音
  • 若导出录音,使用短TTL的签名URL。

PII minimization

PII最小化

  • Do not log full phone numbers unless necessary; mask:
    • +14155551234
      +1415****234
  • Avoid storing DTMF inputs that may contain account numbers unless required; if required, tokenize.

  • 除非必要,否则不要记录完整电话号码;进行掩码处理:
    • +14155551234
      +1415****234
  • 避免存储可能包含账号信息的DTMF输入;若必须存储,需进行令牌化处理。

Performance Tuning

性能调优

Webhook latency budget

Webhook延迟预算

Target: p95 < 200ms for TwiML generation (excluding network).
Optimizations:
  • Precompute static TwiML templates for common branches.
  • Avoid synchronous calls to external services inside webhook.
  • Use in-memory caches for routing tables (with periodic refresh).
Expected impact:
  • Moving CRM lookup out of webhook into async job often reduces p95 from ~800ms to ~120ms.
目标:TwiML生成的p95延迟 < 200ms(不含网络延迟)。
优化方案:
  • 为常见分支预计算静态TwiML模板。
  • 避免在Webhook中同步调用外部服务。
  • 使用内存缓存存储路由表(定期刷新)。
预期效果:
  • 将CRM查询从Webhook移至异步任务,通常可将p95延迟从约800ms降至约120ms。

Reduce Twilio REST API chatter

减少Twilio REST API调用

  • Cache call metadata for short windows (e.g., 30–60 seconds) to avoid repeated
    Calls.fetch
    .
  • For conference participant polling, prefer event-driven callbacks where possible.
Expected impact:
  • 30–70% reduction in REST calls during incident debugging and dashboards.
  • 短时间缓存呼叫元数据(例如30–60秒),避免重复调用
    Calls.fetch
  • 对于会议参会者轮询,优先使用事件驱动的回调。
预期效果:
  • 在故障排查和仪表盘场景中,REST调用减少30–70%。

Conference scaling

会议服务扩容

  • Avoid creating unique conferences per minor interaction; reuse conference rooms per session.
  • Use deterministic conference names (e.g.,
    support-${ticketId}
    ) to simplify correlation.
  • Ensure you handle participant join/leave events idempotently.
  • 避免为次要交互创建唯一会议;按会话复用会议室。
  • 使用确定性会议名称(例如
    support-${ticketId}
    )简化关联。
  • 确保幂等处理参会者加入/离开事件。

Recording download throughput

录音下载吞吐量

  • Download recordings asynchronously with bounded concurrency (e.g., 10–25 workers).
  • Use HTTP keep-alive and streaming to object storage.
Expected impact:
  • Stable worker CPU/memory; avoids 429 rate limits and reduces tail latency.

  • 异步下载录音,限制并发数(例如10–25个工作节点)。
  • 使用HTTP长连接和流式传输至对象存储。
预期效果:
  • 工作节点CPU/内存稳定;避免429速率限制并降低尾部延迟。

Advanced Topics

进阶主题

Idempotency for webhooks

Webhook幂等性

Twilio retries can cause duplicate processing.
Pattern:
  • Compute idempotency key:
    • voice:{CallSid}:{EventType}:{Timestamp}
  • Store in Redis with TTL (e.g., 24h).
  • If key exists, return 200 quickly.
Twilio重试可能导致重复处理。
实现模式:
  • 计算幂等键:
    • voice:{CallSid}:{EventType}:{Timestamp}
  • 将键存储在Redis中并设置TTL(例如24小时)。
  • 若键已存在,快速返回200。

Handling
<Gather>
edge cases

处理
<Gather>
边缘场景

  • Digits
    may be missing on timeout.
  • Users can press multiple digits quickly; enforce
    numDigits
    .
  • For speech, partial results and confidence vary; always provide DTMF fallback.
  • 超时场景下
    Digits
    可能缺失。
  • 用户可能快速按下多个数字;需强制
    numDigits
    限制。
  • 语音输入的部分结果和置信度存在差异;始终提供DTMF降级方案。

<Dial>
child call behavior

<Dial>
子呼叫行为

  • Status callbacks for parent and child calls differ.
  • If you need the dialed leg SID, capture it via callbacks or Twilio events.
  • For transfers, track
    ParentCallSid
    to link legs.
  • 父呼叫和子呼叫的状态回调不同。
  • 若需要被呼叫方的链路SID,通过回调或Twilio事件捕获。
  • 对于转移操作,跟踪
    ParentCallSid
    以关联链路。

Answering machine detection (AMD)

应答机检测(AMD)

  • Adds latency and can misclassify.
  • Use only when business logic requires it (e.g., leave voicemail).
  • Implement fallback: if AMD uncertain, route to human or play prompt.
  • 会增加延迟且可能误分类。
  • 仅当业务逻辑需要时使用(例如留语音信箱)。
  • 实现降级方案:若AMD结果不确定,路由至人工或播放提示音。

Edge locations

边缘节点

  • For latency-sensitive apps, specify Twilio edge in REST client:
    • Node helper supports
      edge
      /
      region
      configuration.
  • Ensure consistent edge selection across services to avoid cross-region media paths.
  • 对于延迟敏感的应用,在REST客户端中指定Twilio边缘节点:
    • Node辅助库支持
      edge
      /
      region
      配置。
  • 确保所有服务选择一致的边缘节点,避免跨区域媒体路径。

Compliance and consent

合规与同意

  • Recording consent requirements vary by jurisdiction.
  • Implement explicit consent prompts before enabling recording when required.
  • Store consent event with timestamp and
    CallSid
    .

  • 录音同意要求因地区而异。
  • 当法律要求时,在启用录音前实现明确的同意提示。
  • 存储同意事件,包含时间戳和
    CallSid

Usage Examples

使用示例

1) Inbound IVR → route to on-call via
<Dial>
with Polly voice

1) 呼入IVR → 通过
<Dial>
路由至值班人员(使用Polly语音)

Node TwiML snippet:
javascript
const vr = new twilio.twiml.VoiceResponse();
vr.say({ voice: "Polly.Matthew", language: "en-US" }, "You have reached incident response.");
vr.dial(
  {
    callerId: process.env.TWILIO_CALLER_ID,
    timeout: 20,
    record: "record-from-answer",
    recordingStatusCallback: "https://voice.prod.example.com/webhooks/recording",
  },
  "+14155550123"
);
res.type("text/xml").send(vr.toString());
Operational notes:
  • Ensure on-call number is E.164.
  • Recording callback must be signature-validated too.

Node TwiML片段:
javascript
const vr = new twilio.twiml.VoiceResponse();
vr.say({ voice: "Polly.Matthew", language: "en-US" }, "You have reached incident response.");
vr.dial(
  {
    callerId: process.env.TWILIO_CALLER_ID,
    timeout: 20,
    record: "record-from-answer",
    recordingStatusCallback: "https://voice.prod.example.com/webhooks/recording",
  },
  "+14155550123"
);
res.type("text/xml").send(vr.toString());
运维注意事项:
  • 确保值班人员号码为E.164格式。
  • 录音回调也需进行签名验证。

2) Outbound call with inline TwiML (REST) + status callbacks

2) 使用内联TwiML(REST)+ 状态回调发起外呼

Using curl:
bash
export TWILIO_ACCOUNT_SID="AC2f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"
export TWILIO_AUTH_TOKEN="9f8e7d6c5b4a3a2b1c0d9e8f7a6b5c4d"

curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json" \
  --data-urlencode "From=+14155551234" \
  --data-urlencode "To=+14155559876" \
  --data-urlencode "Twiml=<Response><Say voice=\"Polly.Joanna\">This is a test call.</Say><Hangup/></Response>" \
  --data-urlencode "StatusCallback=https://voice.prod.example.com/webhooks/voice/status" \
  --data-urlencode "StatusCallbackEvent=initiated ringing answered completed" \
  -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
Notes:
  • Inline TwiML is useful for simple notifications; for complex flows, use a URL.

使用curl:
bash
export TWILIO_ACCOUNT_SID="AC2f1c2d3e4f5a6b7c8d9e0f1a2b3c4d5"
export TWILIO_AUTH_TOKEN="9f8e7d6c5b4a3a2b1c0d9e8f7a6b5c4d"

curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_ACCOUNT_SID/Calls.json" \
  --data-urlencode "From=+14155551234" \
  --data-urlencode "To=+14155559876" \
  --data-urlencode "Twiml=<Response><Say voice=\"Polly.Joanna\">This is a test call.</Say><Hangup/></Response>" \
  --data-urlencode "StatusCallback=https://voice.prod.example.com/webhooks/voice/status" \
  --data-urlencode "StatusCallbackEvent=initiated ringing answered completed" \
  -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN"
注意事项:
  • 内联TwiML适用于简单通知;复杂流程建议使用URL。

3) Warm transfer using conference

3) 使用会议服务实现暖转移

TwiML pattern:
  • Agent leg joins conference muted=false
  • Customer leg joins conference
  • Supervisor can join with
    startConferenceOnEnter=false
    for monitoring
Example TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial>
    <Conference
      startConferenceOnEnter="true"
      endConferenceOnExit="true"
      beep="onEnter"
      record="record-from-start"
      recordingStatusCallback="https://voice.prod.example.com/webhooks/recording"
    >support-ticket-842193</Conference>
  </Dial>
</Response>
Operational notes:
  • Use deterministic conference names to correlate with tickets.
  • Use participant update API to mute/unmute during warm transfer.

TwiML模式:
  • 坐席链路加入会议,静音状态为false
  • 客户链路加入会议
  • 主管可通过
    startConferenceOnEnter=false
    加入会议进行监控
示例TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial>
    <Conference
      startConferenceOnEnter="true"
      endConferenceOnExit="true"
      beep="onEnter"
      record="record-from-start"
      recordingStatusCallback="https://voice.prod.example.com/webhooks/recording"
    >support-ticket-842193</Conference>
  </Dial>
</Response>
运维注意事项:
  • 使用确定性会议名称关联工单。
  • 使用参会者更新API在暖转移过程中静音/取消静音。

4) Voicemail drop with
<Record>
and transcription pipeline

4) 使用
<Record>
实现语音信箱投递并集成转写流水线

TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Joanna">Please leave a message after the tone.</Say>
  <Record
    maxLength="120"
    timeout="5"
    playBeep="true"
    recordingStatusCallback="https://voice.prod.example.com/webhooks/recording"
    recordingStatusCallbackMethod="POST"
  />
  <Say voice="Polly.Joanna">Thank you. Goodbye.</Say>
  <Hangup/>
</Response>
Pipeline:
  • Recording callback enqueues transcription job.
  • Worker downloads
    .wav
    and transcribes.
  • Store transcript keyed by
    RecordingSid
    .

TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Joanna">Please leave a message after the tone.</Say>
  <Record
    maxLength="120"
    timeout="5"
    playBeep="true"
    recordingStatusCallback="https://voice.prod.example.com/webhooks/recording"
    recordingStatusCallbackMethod="POST"
  />
  <Say voice="Polly.Joanna">Thank you. Goodbye.</Say>
  <Hangup/>
</Response>
流水线流程:
  • 录音回调将转写任务加入队列。
  • 工作节点下载
    .wav
    文件并执行转写。
  • 存储转写结果并关联
    RecordingSid

5) SIP inbound → route to PSTN fallback

5) SIP呼入 → 路由至PSTN降级方案

TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Matthew">Routing your SIP call.</Say>
  <Dial callerId="+14155551234" timeout="15">
    <Number>+14155550199</Number>
  </Dial>
  <Say voice="Polly.Matthew">We could not connect your call.</Say>
  <Hangup/>
</Response>
Notes:
  • Ensure SIP domain is configured to hit this TwiML app/webhook.
  • Monitor SIP response codes and RTP stats if available.

TwiML:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="Polly.Matthew">Routing your SIP call.</Say>
  <Dial callerId="+14155551234" timeout="15">
    <Number>+14155550199</Number>
  </Dial>
  <Say voice="Polly.Matthew">We could not connect your call.</Say>
  <Hangup/>
</Response>
注意事项:
  • 确保SIP域名配置为指向该TwiML应用/Webhook。
  • 若可用,监控SIP响应码和RTP统计数据。

6) IVR state machine with retries (DTMF)

6) 带重试机制的IVR状态机(DTMF)

Pseudo-implementation approach:
  • State stored in Redis:
    • key:
      ivr:{CallSid}
    • value:
      {state, retries}
  • On
    /voice/inbound
    , set state
    MENU
    retries 0.
  • On
    /voice/menu
    , validate digit:
    • if invalid and retries < 2: increment retries, redirect to
      /voice/inbound
    • else: route to operator or hang up
This avoids infinite loops and makes behavior deterministic.

伪实现思路:
  • 状态存储在Redis中:
    • 键:
      ivr:{CallSid}
    • 值:
      {state, retries}
  • /voice/inbound
    中设置状态为
    MENU
    ,重试次数为0。
  • /voice/menu
    中验证输入的数字:
    • 若无效且重试次数 < 2:增加重试次数,重定向至
      /voice/inbound
    • 否则:路由至人工或挂断
此方案避免无限循环,确保行为确定性。

Quick Reference

快速参考

TaskCommand / APIKey flags / fields
Create outbound call
twilio api:core:calls:create
--from
,
--to
,
--url
/
--twiml
,
--status-callback
,
--timeout
List calls
twilio api:core:calls:list
--limit
,
--properties
Fetch call
twilio api:core:calls:fetch
--sid
List recordings
twilio api:core:recordings:list
--limit
,
--properties
Download recording
curl -u SID:TOKEN -L .../Recordings/{RE}.wav
-L
, output file
List conferences
twilio api:core:conferences:list
--status
,
--limit
List participants
twilio api:core:conferences:participants:list
--conference-sid
,
--limit
Mute participant
twilio api:core:conferences:participants:update
--conference-sid
,
--sid
,
--muted true
Debug webhook failuresTwilio Console Debuggerlook for
11200
,
12100
,
12300
Validate webhookhelper libs
X-Twilio-Signature
, exact URL

任务命令 / API关键参数 / 字段
创建外呼
twilio api:core:calls:create
--from
,
--to
,
--url
/
--twiml
,
--status-callback
,
--timeout
列出呼叫记录
twilio api:core:calls:list
--limit
,
--properties
获取单个呼叫详情
twilio api:core:calls:fetch
--sid
列出录音记录
twilio api:core:recordings:list
--limit
,
--properties
下载录音
curl -u SID:TOKEN -L .../Recordings/{RE}.wav
-L
, 输出文件
列出会议
twilio api:core:conferences:list
--status
,
--limit
列出参会者
twilio api:core:conferences:participants:list
--conference-sid
,
--limit
静音参会者
twilio api:core:conferences:participants:update
--conference-sid
,
--sid
,
--muted true
调试Webhook故障Twilio Console Debugger查找
11200
,
12100
,
12300
错误码
验证Webhook辅助库
X-Twilio-Signature
, 精确URL

Graph Relationships

依赖关系

DEPENDS_ON

DEPENDS_ON

  • twilio-core
    (Account auth, REST API fundamentals, subaccounts)
  • http-webhooks
    (signature validation, retries, idempotency)
  • tls-nginx
    (TLS termination, reverse proxy correctness)
  • observability
    (structured logs, metrics, tracing)
  • twilio-core
    (账号认证、REST API基础、子账号)
  • http-webhooks
    (签名验证、重试、幂等性)
  • tls-nginx
    (TLS终止、反向代理正确性)
  • observability
    (结构化日志、指标、追踪)

COMPOSES

COMPOSES

  • twilio-messaging
    (SMS fallback, status webhooks, STOP handling)
  • twilio-verify
    (voice OTP, fraud guard, rate limiting)
  • sendgrid
    (email notifications for missed calls/voicemails)
  • twilio-studio
    (hybrid flows: Studio for rapid iteration + code for critical paths)
  • queue-workers
    (recording/transcription pipelines)
  • twilio-messaging
    (SMS降级、状态Webhook、退订处理)
  • twilio-verify
    (语音OTP、欺诈防护、速率限制)
  • sendgrid
    (未接来电/语音信箱的邮件通知)
  • twilio-studio
    (混合流程:Studio快速迭代 + 代码处理关键路径)
  • queue-workers
    (录音/转写流水线)

SIMILAR_TO

SIMILAR_TO

  • plivo-voice
    (similar call control concepts, different APIs)
  • vonage-voice
    (voice webhooks + NCCO vs TwiML)
  • aws-connect
    (contact center flows; heavier platform abstraction)
  • plivo-voice
    (类似呼叫控制概念,API不同)
  • vonage-voice
    (语音Webhook + NCCO vs TwiML)
  • aws-connect
    (联络中心流程;更重的平台抽象)