You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

twilio/voice-sdk拨打Twilio号码时无法触发incoming来电监听问题

当前使用技术栈及版本
  • Laravel (9.11)
  • vue.js (2.x)
  • php (8.1.0)
  • twilio/voice-sdk (2.1.1)
  • twilio/sdk (6.37)
应用业务流程

基于TaskRouter搭建语音呼入联络中心,客户通过手机拨打联络中心接入号码(+1 873 --- 0331)发起呼叫。

实现步骤

步骤1:IVR入口Webhook

用户拨打接入号码时触发语音webhook,执行如下IVR逻辑代码:

public function webhookForContactCenterBaseNumber(Request $request)
    {
        $response = new VoiceResponse();
        $params = array();
        $params['action'] = secure_url('/api/webhook-for-contact-center-ivr');
        $params['numDigits'] = 1;
        $params['timeout'] = 10;
        $params['method'] = "POST";

        $gather = $response->gather($params);

        $gather->say('For Spanish, please press one.', ['language' => 'es']);
        $gather->say('For Enghlish,please press two.', ['language' => 'en']);

        return $response;
    }

步骤2:按键处理与任务创建

用户按下数字键(1/2)后,通过TaskRouter结合工作流创建任务,对应代码如下:

public function webhookForContactCenterIvr(Request $request)
    {
        $response = new VoiceResponse();
        $digits = $request['Digits'];
        $language = $digits == 1 ? 'es' : 'en';

        switch ($digits) {
            case 1 || 2:
                $response->enqueue(null, [
                    'waitUrl' => 'http://twimlets.com/holdmusic?Bucket=com.twilio.music.classical',
                    'workflowSid' => 'WW456fb07f4fdc4f55779dcb6bd90f9273'
                ])
                    ->task(json_encode([
                        'selected_language' => $language,
                    ]));
                break;
            default:
                $response->say("Sorry, Caller. You can only press 1 for spanish, or 2 for english.");
                break;
        }

        return $response;
    }

步骤3:坐席分配回调

任务创建完成后,在控制台手动将目标坐席状态设置为标签为idle的可用状态,随后触发如下分配回调webhook。根据官方文档说明,此时应通过Twilio caller id桥接主叫用户与坐席的Twilio号码建立通话:

public function assigment(Request $request)
    {

        $assignment_instruction = [
            'instruction' => 'dequeue',
            'post_work_activity_sid' => 'WA92871fe67075e6556c02e92de6---924',
            'from' => '+1647---4676' // Twilio账户下的已验证号码
        ];


        return $this->respond($assignment_instruction, ['Content-Type', 'application/json']);
    }

通话日志截图

通话日志截图

步骤4:后端令牌生成逻辑

后端Twilio服务相关控制器代码如下:

namespace App\Http\Controllers\Api;

use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VoiceGrant;
use Illuminate\Http\Request;
use Twilio\Rest\Client;

class TwilioController extends ApiController
{

    // Twilio访问令牌必填配置
    private $twilioAccountSid;
    private $twilioAccountAuthToken;
    private $twilioApiKey;
    private $twilioApiSecret;
    private $identity;


    public function __construct()
    {
        $this->twilioAccountSid = config('general.twilio_account_sid');
        $this->twilioAccountAuthToken = config('general.twilio_auth_token');

        $this->twilioApiKey = 'SK45e57c57f923e5c3c0903f48b70ba9de';
        $this->twilioApiSecret = 'uqDNnlnDZbWZCKBwlmMdlMIIonhh3X3K';
        // 为接入用户设置固定身份标识
        $this->identity = 'daffdfwerweds';
    }

    public function getCallAccessToken()
    {
        $token = new AccessToken(
            $this->twilioAccountSid,
            $this->twilioApiKey,
            $this->twilioApiSecret,
            3600,
            $this->identity
        );

        // 创建语音权限授权
        $voiceGrant = new VoiceGrant();

        // 开启来电接收权限
        $voiceGrant->setIncomingAllow(true);

        // 将授权写入令牌
        $token->addGrant($voiceGrant);

        return $this->respond([
            'status' => true,
            'message' => '',
            'data' => [
                'accessToken' => $token->toJWT()
            ]
        ]);
    }

    public function getTwilioKey($frindlyName)
    {

        $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
        return $twilio->newKeys->create(["friendlyName" => $frindlyName]);
    }
    public function getKeys()
    {
        $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
        $keys = $twilio->keys
            ->read(20);

        foreach ($keys as $record) {
            $twilio->keys($record->sid)
                ->delete();
        }
    }
    public function getAllCalls(Request $request)
    {
        $twilio = new Client($this->twilioAccountSid, $this->twilioAccountAuthToken);
        $calls = $twilio->calls
            ->read([], 20);

        foreach ($calls as $record) {
            $twilio->calls($record->sid)
                ->delete();
        }
    }
}

步骤5:前端SDK注册逻辑

在Vue项目中安装twilio/voice-sdk,使用如下代码注册Device实例:

const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImN0eSI6InR3aWxpby1mcGE7dj0xIn0.eyJqdGkiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlLTE2NTU3MzgxNjMiLCJpc3MiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlIiwic3ViIjoiQUMwMWExYTRmMDdjMGMwMDlhMmIyZTEyYmJkZWVhYjQ2NSIsImV4cCI6MTY1NTc0MTc2MywiZ3JhbnRzIjp7ImlkZW50aXR5IjoiZGFmZmRmd2Vyd2VkcyIsInZvaWNlIjp7ImluY29taW5nIjp7ImFsbG93Ijp0cnVlfX19fQ.4COIn-EQMQnD6alKUSOZPGIWG3jB5k17K418xCsSiZs"

    const device = new Device(accessToken, {
      logLevel: 1,
      // 优先设置Opus编码,该编码带宽占用更低、网络受限场景下音质表现更好
      codecPreferences: ["opus", "pcmu"]
    });

    const handleSuccessfulRegistration = () => {
      console.log('The device is ready to receive incoming calls.')
    }

    device.register();
    device.on('registered', handleSuccessfulRegistration);


    device.on('error', (twilioError, call) => {
      console.log('An error has occurred: ', twilioError);
    });

    device.on('incoming', call => {
      console.log('call received-----------------')
    });
验证结果
  • JWT令牌校验结果:
    JWT校验结果截图
  • 控制台设备注册测试结果:
    设备注册测试结果截图
当前问题

设备已成功注册、Token已开启来电允许权限,但实际拨打Twilio号码测试时,Device实例的incoming事件始终无法触发,无法接收到来电回调。


内容的提问来源于stack exchange,提问作者shafeeque ahmad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 00:01:05