如何通过Twilio Voice实现通话录音转写及正确外呼?
解决方案:单通外呼+录音转写+避免双重计费
一、录音转写的正确配置
不要在client.calls.create里单独设置record:true,全程录音转写需要通过<Dial>标签的参数实现,而非单独的<Record>标签(后者仅用于录制通话片段)。
直接在创建通话时传入包含转写配置的TwiML,示例如下:
<Response> <Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result"> <Number>{客户号码}</Number> </Dial> </Response>
record="record-from-answer":从通话接通开始全程录音transcribe="true":启用自动转写transcribeCallback:指定接收转写结果的API端点(需公开可访问)
二、消除双重通话/计费问题
你遇到的两次通话是因为现有逻辑让Twilio先呼叫你的应用端,再由应用端触发呼叫客户,形成两条链路。解决方法是让Twilio直接从你的号码外呼客户:
NextJS API端点示例代码:
import twilio from 'twilio'; export default async function handler(req, res) { const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN); const call = await client.calls.create({ from: process.env.TWILIO_PHONE_NUMBER, // 你的Twilio号码 to: req.body.customerNumber, // 客户号码 twiml: `<Response><Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result"><Number>${req.body.customerNumber}</Number></Dial></Response>` }); res.status(200).json({ callSid: call.sid }); }
此逻辑仅生成一次从Twilio号码到客户的通话,避免双重计费。
三、前端直连外呼(@twilio/voice-sdk)实现
1. 后端生成AccessToken
创建/api/get-token端点,生成包含外呼权限的Token:
import twilio from 'twilio'; import { AccessToken } from 'twilio/lib/jwt/AccessToken'; import { VoiceGrant } from 'twilio/lib/jwt/AccessToken'; export default function handler(req, res) { const accountSid = process.env.TWILIO_ACCOUNT_SID; const authToken = process.env.TWILIO_AUTH_TOKEN; const appSid = process.env.TWILIO_TWIML_APP_SID; // 关联你的TwiML App const identity = 'user-' + Date.now(); // 唯一用户标识 const voiceGrant = new VoiceGrant({ outgoingApplicationSid: appSid, incomingAllow: false // 关闭呼入,仅允许外呼 }); const token = new AccessToken(accountSid, process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET); token.addGrant(voiceGrant); token.identity = identity; res.status(200).json({ token: token.toJwt() }); }
2. 前端初始化Device并发起呼叫
import { Device } from '@twilio/voice-sdk'; let device; // 初始化设备 const initDevice = async () => { const res = await fetch('/api/get-token'); const { token } = await res.json(); device = new Device(token, { audioConstraints: { audio: true } }); device.on('ready', () => console.log('设备就绪')); device.on('error', (err) => console.error('设备错误:', err)); }; // 发起外呼按钮点击事件 const makeOutboundCall = (customerNumber) => { if (!device) return; const call = device.connect({ params: { To: customerNumber } }); call.on('connect', () => console.log('通话接通')); call.on('disconnect', () => console.log('通话结束')); }; // 页面加载时初始化设备 initDevice();
3. TwiML App的语音端点配置
创建/api/voice端点,根据前端传入的To参数返回Dial指令:
export default function handler(req, res) { const to = req.query.To; const twiml = ` <Response> <Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result"> <Number>${to}</Number> </Dial> </Response> `; res.setHeader('Content-Type', 'text/xml'); res.status(200).send(twiml); }
关键注意事项
- 确保Twilio账号已开启语音转写功能(控制台>语音>设置)
transcribeCallback端点需公开可访问,开发环境可使用ngrok做隧道转发- 不要同时在
client.calls.create和TwiML中设置录音参数,避免重复录音
内容的提问来源于stack exchange,提问作者daveidivide
相关产品推荐
相关产品推荐

