基于Twilio的呼叫转接触发应用错误12100求助
Twilio呼叫自动化系统Error 12100(文档解析失败)排查与修复
问题概述
开发基于Twilio的呼叫自动化系统,需实现以下功能:
- 发起外呼呼叫
- 通话过程中播放音频文件
- 用户按下“1”时转接至指定号码
运行代码时通话出现“应用错误”,Twilio调试器显示错误码12100,对应文档解析失败,问题源于TwiML的生成与返回逻辑存在缺陷。
错误原因分析
- 初始TwiML地址为空:发起呼叫时
url参数未设置,Twilio无法获取初始通话流程的TwiML文档 - Gather动作地址缺失:
<Gather>标签的action属性为空,用户按键后Twilio无法找到处理请求的端点 - 响应内容混杂:
handleKeyPress函数中先输出文本信息,再输出TwiML,导致响应内容不符合XML规范,Twilio无法解析 - 未指定请求方法:
<Gather>未明确method="POST",可能导致Twilio无法正确传递按键数据
修复方案
- 填充呼叫初始化时的
url参数,指向服务器上返回初始TwiML的接口地址 - 为
<Gather>标签设置action属性和method="POST",指定按键处理接口 - 移除
handleKeyPress中的非XML输出文本,确保响应仅返回标准TwiML - 确保所有Twilio凭据、目标号码、音频URL等变量已正确填充
- 验证TwiML输出前无任何多余字符(如空格、换行),保证XML结构完整
修正后的完整代码
<?php // Enable error reporting for debugging ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require 'vendor/autoload.php'; // Include Twilio SDK use Twilio\Rest\Client; // Twilio credentials - 请填充实际值 $twilioSid = 'your_twilio_sid'; // Your Twilio SID $twilioToken = 'your_twilio_auth_token'; // Your Twilio Auth Token $twilioFromNumber = 'your_twilio_phone_number'; // Your Twilio phone number $forwardNumber = 'your_forward_number'; // Number to forward to if '1' is pressed $audioFileUrl = 'your_audio_file_url'; // URL of your audio file $baseUrl = 'https://your-domain.com/your-script.php'; // 替换为你的脚本访问地址 // Function to start the call function startCalling() { global $twilioSid, $twilioToken, $twilioFromNumber, $baseUrl; // Phone numbers to call - 填充实际呼叫号码 $numbersToCall = ['target_phone_number']; // Example number // Create a Twilio client $client = new Client($twilioSid, $twilioToken); foreach ($numbersToCall as $toPhoneNumber) { try { // Initiate the call and listen for keypresses $call = $client->calls->create( $toPhoneNumber, // To $twilioFromNumber, // From [ 'url' => $baseUrl . '?action=serve_twiml', // 指向初始TwiML接口 ] ); echo "Call initiated to: " . $toPhoneNumber . " with SID: " . $call->sid . "<br>"; } catch (Exception $e) { echo "Error while calling " . $toPhoneNumber . ": " . $e->getMessage() . "<br>"; } } } // Function to serve TwiML function serveTwiml() { global $audioFileUrl, $baseUrl; // 确保在输出XML前无任何内容 ob_clean(); // Output XML response header('Content-Type: text/xml'); echo '<?xml version="1.0" encoding="UTF-8"?>'; echo '<Response>'; // 设置Gather的action和method,指向按键处理接口 echo '<Gather numDigits="1" action="' . $baseUrl . '?action=handle_key_press" method="POST">'; echo '<Play>' . htmlspecialchars($audioFileUrl) . '</Play>'; echo '</Gather>'; // 用户未按键时的 fallback,重复播放音频 echo '<Play>' . htmlspecialchars($audioFileUrl) . '</Play>'; echo '</Response>'; exit; } // Function to handle keypresses function handleKeyPress() { global $forwardNumber, $audioFileUrl; $Digits = $_POST['Digits'] ?? ''; // 确保在输出XML前无任何内容 ob_clean(); header('Content-Type: text/xml'); echo '<?xml version="1.0" encoding="UTF-8"?>'; if ($Digits == '1') { // 直接返回Dial的TwiML,无需通过API更新呼叫(更高效) echo '<Response><Dial>' . htmlspecialchars($forwardNumber) . '</Dial></Response>'; } else { // 未按1时,返回重新播放音频的TwiML echo '<Response><Gather numDigits="1" action="' . $_SERVER['REQUEST_URI'] . '" method="POST">'; echo '<Play>' . htmlspecialchars($audioFileUrl) . '</Play>'; echo '</Gather></Response>'; } exit; } // Handle POST requests if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_GET['action'])) { switch ($_GET['action']) { case 'handle_key_press': handleKeyPress(); break; } } elseif (isset($_POST['action'])) { switch ($_POST['action']) { case 'start_calling': startCalling(); break; } } } // Handle GET requests for serving TwiML if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action'])) { if ($_GET['action'] === 'serve_twiml') { serveTwiml(); } } ?>
额外说明
- 替换代码中所有标注
请填充实际值的变量,确保Twilio凭据、号码、音频URL、脚本访问地址正确 - 使用
ob_clean()清除输出缓冲区,避免意外的空白字符破坏XML结构 - 按键处理时直接返回TwiML而非通过API更新呼叫,更符合Twilio的流程设计,也减少了出错概率
内容的提问来源于stack exchange,提问作者vaters suntazs
相关产品推荐
相关产品推荐

