I am using the following stack with versions
Laravel (9.11)
vue.js (2.x)
php (8.1.0)
twilio/voice-sdk (2.1.1)
twilio/sdk (6.37)
Workflow of my application:
I am making an inbound contact center for voice calls by using a task router, where a customer initiates the call from his/her phone to our contact center base number(+1 873 --- 0331)
Step #1
when the user call on this number(+1 873 --- 0331) voice webhook is called with the following code for 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;
}
Step #2
When the user presses A digit(1/2) I create a task with workflow via the task router
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;
}
step #3
After task creation, I make the targeted agent available manually from the console with the label ‘idle’, then following webhook called.
According to documentation bridge call was created between caller and agent Twilio phone number via Twilio caller id
public function assigment(Request $request)
{
$assignment_instruction = [
'instruction' => 'dequeue',
'post_work_activity_sid' => 'WA92871fe67075e6556c02e92de6---924',
'from' => '+1647---4676' // a verified phone number from your twilio account
];
return $this->respond($assignment_instruction, ['Content-Type', 'application/json']);
}
step #4
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
{
// Required for all Twilio access tokens
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';
// choose a random username for the connecting user
$this->identity = 'daffdfwerweds';
}
public function getCallAccessToken()
{
$token = new AccessToken(
$this->twilioAccountSid,
$this->twilioApiKey,
$this->twilioApiSecret,
3600,
$this->identity
);
// Create Voice grant
$voiceGrant = new VoiceGrant();
// Optional: add to allow incoming calls
$voiceGrant->setIncomingAllow(true);
// Add grant to token
$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) {
// print($record->sid);
$twilio->calls($record->sid)
->delete();
}
}
}
Step #5 I have installed twilio/voice-sdk in vue and register my device with following code
const accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImN0eSI6InR3aWxpby1mcGE7dj0xIn0.eyJqdGkiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlLTE2NTU3MzgxNjMiLCJpc3MiOiJTSzQ1ZTU3YzU3ZjkyM2U1YzNjMDkwM2Y0OGI3MGJhOWRlIiwic3ViIjoiQUMwMWExYTRmMDdjMGMwMDlhMmIyZTEyYmJkZWVhYjQ2NSIsImV4cCI6MTY1NTc0MTc2MywiZ3JhbnRzIjp7ImlkZW50aXR5IjoiZGFmZmRmd2Vyd2VkcyIsInZvaWNlIjp7ImluY29taW5nIjp7ImFsbG93Ijp0cnVlfX19fQ.4COIn-EQMQnD6alKUSOZPGIWG3jB5k17K418xCsSiZs"
const device = new Device(accessToken, {
logLevel: 1,
// Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
// providing better audio quality in restrained network conditions.
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-----------------')
});
Verify token on jwt.io
I was facing the same issue, thanks for detailed information, I go through the whole detail, and here is the answer after that issue will be fixed,
in step #4 you are creating call access token, and you are adding worker/agent identity you need to add some identity against the worker inside the Twilio console, in your case, it should be like that, in code
in Twilio console under task router/workspace/workers/open target work
most important part
Your browser will listen the incoming call via SDK if call router toward you this worker. that's all.