Twilio Voice Twiml

作者 twilio8aba46fb65dc无许可证收录于 2026年10月8日更新于 2026年10月8日

Build voice call logic using TwiML (Twilio Markup Language). Covers the core verbs (Say, Play, Gather, Dial, Record, Conference), generating TwiML with Python and Node.js SDKs, and a complete inbound call IVR example. Use this skill to define call behavior for inbound or outbound calls.

AI 生成的概览

使用 TwiML 构建 Twilio 语音通话逻辑,涵盖核心动词、SDK 生成方式和 IVR 示例。

功能
该技能说明如何使用 TwiML(Twilio 在通话期间执行的 XML)定义呼入和呼出通话行为。它记录了 Say、Play、Gather、Dial、Record、Conference 和 Pay 等核心动词,并提供 Python 与 Node.js SDK 示例以及完整的呼入 IVR 流程。内容还涉及 webhook 托管、请求间的无状态处理、监控、请求参数和已知限制。
适用场景
适用于实现从 webhook 返回 TwiML 的通话处理逻辑,例如 IVR 菜单、呼叫转接、语音留言或会议设置。也适用于使用 Twilio Python 或 Node.js SDK 以编程方式生成 TwiML。
运行要求
需要具备语音功能的电话号码的 Twilio 账户、可公开访问并返回 text/xml 的 HTTPS webhook 端点,以及可选地通过 pip 或 npm 安装的 Twilio SDK。该技能不包含脚本,仅为说明文档。

Overview

TwiML is XML that Twilio executes during a call. Your server returns a TwiML document in response to a Twilio webhook POST, and Twilio executes it.

Caller → Twilio → POST to your webhook → Your server returns TwiML → Twilio executes it

Prerequisites

  • Twilio account with a voice-capable phone number — New to Twilio? See twilio-account-setup
  • Webhook endpoint returning TwiML with Content-Type: text/xml
  • SDK (for programmatic generation): pip install twilio / npm install twilio

Quickstart

A minimal inbound call handler that greets the caller and presents a menu:

Python (Flask)

python
from flask import Flask, requestfrom twilio.twiml.voice_response import VoiceResponse
app = Flask(__name__)
@app.route("/voice", methods=["POST"])def handle_call():    response = VoiceResponse()    gather = response.gather(num_digits=1, action="/menu-choice")    gather.say("Welcome to Acme. Press 1 for sales, 2 for support.")    response.redirect("/voice")  # Loop if no input    return str(response)
@app.route("/menu-choice", methods=["POST"])def menu_choice():    digit = request.form.get("Digits")    response = VoiceResponse()    if digit == "1":        response.dial("+15551234567")    elif digit == "2":        response.say("Connecting to support.")        response.dial("+15559876543")    else:        response.say("Invalid option.")        response.redirect("/voice")    return str(response)

Node.js (Express)

node
const { VoiceResponse } = require("twilio").twiml;
app.post("/voice", (req, res) => {    const response = new VoiceResponse();    const gather = response.gather({ numDigits: 1, action: "/menu-choice" });    gather.say("Welcome. Press 1 for sales, 2 for support.");    response.redirect("/voice");    res.type("text/xml").send(response.toString());});
app.post("/menu-choice", (req, res) => {    const digit = req.body.Digits;    const response = new VoiceResponse();    if (digit === "1") response.dial("+15551234567");    else response.say("Invalid option.").redirect("/voice");    res.type("text/xml").send(response.toString());});

Core Verbs

Say — Text-to-speech

Python

python
from twilio.twiml.voice_response import VoiceResponse
response = VoiceResponse()response.say("Your appointment is confirmed.", voice="alice", language="en-US")

Node.js

node
const { VoiceResponse } = require("twilio").twiml;const response = new VoiceResponse();response.say({ voice: "alice", language: "en-US" }, "Your appointment is confirmed.");

Voices: alice (default), man, woman, or Polly/Google TTS (e.g. Polly.Joanna).

Gather — Collect keypad input or speech

Python

python
response = VoiceResponse()gather = response.gather(num_digits=1, action="/handle-input", method="POST")gather.say("Press 1 for sales, press 2 for support.")response.say("We did not receive your input.")  # Fallback if no input

Node.js

node
const gather = response.gather({ numDigits: 1, action: "/handle-input", method: "POST" });gather.say("Press 1 for sales, press 2 for support.");response.say("We did not receive your input.");

Twilio POSTs collected digits to action as Digits parameter.

Play — Play an audio file

Python

python
response = VoiceResponse()response.play("https://example.com/audio/greeting.mp3")

Node.js

node
const response = new VoiceResponse();response.play("https://example.com/audio/greeting.mp3");

Supported formats: MP3, WAV. URL must be publicly accessible.

Dial — Connect to another number

Python

python
from twilio.twiml.voice_response import Dial
response = VoiceResponse()dial = Dial(action="/dial-complete")dial.number("+15558675310")response.append(dial)

Node.js

node
const dial = response.dial({ action: "/dial-complete" });dial.number("+15558675310");

Record — Capture caller audio

Python

python
response = VoiceResponse()response.say("Leave a message after the beep.")response.record(    action="/recording-complete",    max_length=60,    transcribe=True,    transcribe_callback="/transcription-ready")

Node.js

node
const response = new VoiceResponse();response.say("Leave a message after the beep.");response.record({    action: "/recording-complete",    maxLength: 60,    transcribe: true,    transcribeCallback: "/transcription-ready",});

Voicemail — Record a message when no one answers

Use <Dial> with action URL + <Record> in the action handler. When the dial times out or the callee is busy, the action URL serves TwiML with <Record>.

Python

python
# Primary TwiML — try to connect the callresponse = VoiceResponse()dial = Dial(action="/voicemail", timeout=20)  # 20 seconds before voicemaildial.number("+15558675310")response.append(dial)
# /voicemail handler — plays if no answerdef voicemail_handler(request):    response = VoiceResponse()    response.say("We missed your call. Please leave a message after the beep.")    response.record(        action="/recording-complete",        max_length=120,        transcribe=True,        transcribe_callback="/transcription-ready",        play_beep=True    )    response.say("We didn't receive a recording. Goodbye.")    return str(response)

Node.js

node
// Primary TwiML — try to connect the callconst response = new VoiceResponse();const dial = response.dial({ action: "/voicemail", timeout: 20 });dial.number("+15558675310");
// /voicemail handler — plays if no answerapp.post("/voicemail", (req, res) => {    const response = new VoiceResponse();    response.say("We missed your call. Please leave a message after the beep.");    response.record({        action: "/recording-complete",        maxLength: 120,        transcribe: true,        transcribeCallback: "/transcription-ready",        playBeep: true,    });    response.say("We didn't receive a recording. Goodbye.");    res.type("text/xml").send(response.toString());});

Important: <Record> captures the caller only (voicemail-style). It is NOT for recording two-party calls — see twilio-call-recordings for that.

Conference — Multi-party calls

Python

python
response = VoiceResponse()dial = response.dial()dial.conference(    "Daily Standup",    start_conference_on_enter=True,    end_conference_on_exit=True)

Node.js

node
const response = new VoiceResponse();const dial = response.dial();dial.conference("Daily Standup", {    startConferenceOnEnter: true,    endConferenceOnExit: true,});

Pay — PCI-compliant payment collection

Critical warnings:

  • Pay Connectors are Console-only — there is no REST API to create or manage connectors. Set up in Console > Voice > Pay Connectors before coding.
  • PCI Mode is IRREVERSIBLE once enabled on an account. Use a dedicated sub-account for payment calls.

Python

python
response = VoiceResponse()response.say("We'll now collect your payment.")pay = Pay(    payment_connector="stripe_connector",  # Name from Console setup    charge_amount="49.99",    currency="usd",    action="/payment-complete",    status_callback="/payment-status")response.append(pay)

Node.js

node
const response = new VoiceResponse();response.say("We'll now collect your payment.");response.pay({    paymentConnector: "stripe_connector",    chargeAmount: "49.99",    currency: "usd",    action: "/payment-complete",    statusCallback: "/payment-status",});

Supported processors: Stripe, Braintree, CardConnect. Card data routes directly to the processor — never touches your server.


Production Deployment

Webhook Hosting

For production, do NOT use ngrok. Deploy your TwiML server with HTTPS:

  • Requirement: Public HTTPS URL, responds within 15 seconds, returns Content-Type: text/xml
  • Options: Cloud Run, AWS Lambda + API Gateway, Railway, Render — any service with TLS and auto-scaling
  • Fallback URL: Configure in Console (Phone Numbers > Active Numbers > select number) for when your primary server is unreachable

State Between TwiML Requests

Each webhook request is stateless. To maintain conversation state across interactions:

  • URL query params: Pass state in action URLs — /next-step?language=es&dept=sales
  • Session store: Use Redis or a database keyed by CallSid
  • Do NOT use in-memory state — your server may scale to multiple instances

Monitoring

  • Status callbacks: Track call lifecycle events (statusCallback on the call or number config)
  • Voice Insights: Automatic quality metrics per call (Console > Monitor > Insights)
  • Debugger: Console > Monitor > Errors for TwiML parsing failures and webhook timeouts
  • Fallback URLs: Always configure a fallback TwiML URL — serves a graceful message if your primary endpoint fails

Webhook Request Parameters

ParameterDescription
CallSidUnique call identifier
FromCaller's number
ToCalled number
CallStatusCurrent status
Directioninbound or outbound-api

CANNOT

  • Cannot return TwiML without correct content type — Must use Content-Type: text/xml
  • Cannot exceed 15-second webhook response time — Twilio times out and falls back
  • Cannot exceed 4,096 characters in <Say> verb — Split longer text across multiple <Say> elements
  • Cannot create Pay Connectors via API — Pay Connectors are Console-only (Console > Voice > Pay Connectors). No REST API exists for connector management.
  • Cannot reverse PCI Mode — Once enabled on an account, PCI Mode is permanent and account-wide. Use a dedicated sub-account for payment calls.
  • Cannot use <Record> for two-party call recording — <Record> captures the caller only (voicemail-style). For dual-channel recording of both parties, use record=True on calls.create() or the Recordings API.

Next Steps

  • Place outbound calls (AMD, conferencing): twilio-voice-outbound-calls
  • AI voice agents with real-time speech/LLM: twilio-voice-conversation-relay

来源与署名

来源:twilio/ai位于skills/twilio/twilio-voice-twiml提交8aba46f

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架

更多来自 twilio/ai 的技能

Twilio Isv Sms Best Practices

twilio

面向 ISV 的多租户 Twilio SMS 构建指南,涵盖 A2P 与免付费号码注册、子账户及常见陷阱。

Software Development2026年10月8日

Twilio Security Compliance Hipaa

twilio

指导为 HIPAA 合规配置 Twilio 账户,涵盖 BAA、HIPAA 项目指定、合格服务及各产品要求。

Security2026年10月8日

Twilio Reliability Patterns

twilio

Handle rate limits, retries, and failures when building on Twilio at scale. Covers 429 exponential backoff with jitter, per-number throughput limits, StatusCallback resilience, thin-receiver pattern, and fallback chains. Use this skill whenever sending messages or making calls at volume, or when building production-grade Twilio integrations.

待分类2026年10月8日

Twilio Organizations Setup

twilio

Set up and manage Twilio Organizations for centralized account and user governance. Covers the Organization > Account > Subaccount hierarchy, roles (Owner/Admin/Standard), managed vs independent accounts, domain registration, SSO enforcement, SCIM provisioning, and Organization merging. Use this skill when managing multiple Twilio accounts or users across teams.

待分类2026年10月8日

Twilio Numbers Senders

twilio

指导在开发前选择合适的 Twilio 号码类型和发送方,涵盖合规计划、吞吐量和可用性。

Communication2026年10月8日

Twilio Notifications Alerts Advisor

twilio

为开发者提供 Twilio 事务性通知与告警架构的规划建议,涵盖渠道、紧急程度与降级方案。

Productivity & Workflow2026年10月8日