'Twilio Redirecting the wrong call when using CallSid and trying to update live call

So here's the logic of what I am working on:

  1. Someone calls my Twilio Number I use the dial twiml to forward to a cell phone
  2. I use gather and play a whisper to the operator answering the phone (so his cell phone).
  3. The operator has a choice - press 1 to accept, press 2 (transfers to a different agent).

step 3 is where I am having trouble I am using the code below:

$call = $twilio->calls($CallSid)
           ->update([
                        "method" => "POST",
                        "url" => "http:www.example.com/directcall.php"
                    ]
           );

Here's the problem it is modifying the call but it's redirecting the operators phone number instead of the person who is calling in. So the operator is getting redirected to the other operator and the customer is being hung up on. I tried using the parentcallsid too but that doesn't seem to work either.

Any ideas what I am doing wrong?

so just to be clear I want the flow to work like this:

Customer calls phone number -> redirects to designated operator -> if designated operator presses 2 it redirects the customer to operator 2 and disconnects operator 1 from the call. Is this possible?

Thanks for the help, I greatly appreciate it.

UPDATE PLEASE FIND THE CODE SAMPLES BELOW

Index.php



<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;

$response = new VoiceResponse();


$twilionumber = ltrim($_POST['To'], '+');
$callernumber=ltrim($_POST['From'], '+');

createCall($phonenumbertouse,$response,$twilionumber);




echo $response;





function createCall($phonenumbertouse,$response,$twilionumber) {
 

$dial = $response->dial('',['timeout' => '30']); 



    $dial->number($phonenumbertouse, ['action' => "http://example.com/whisper.php",'method' => 'GET']);  
  
}


WHISPER.PHP


<?php
include ("config.php");

require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;

$response = new VoiceResponse();


$gather = $response->gather(['action' => "http://example.com/route.php",
    'method' => 'GET']);
$gather->say($whisper, ['voice' => 'woman', 'language' => 'en-US']);

echo $response;

?>

route.php




<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;
use Twilio\Rest\Client;
$response = new VoiceResponse();
$keyedInput=$_REQUEST['Digits'];
$mycallsid=$_REQUEST['ParentCallSid'];



if ($keyedInput == 1){
     $response->say('connecting the call');
}
elseif ($keyedInput == 2){
    $twilio = new Client($sid, $token);
    $call = $twilio->calls($mycallsid)
                   ->update([
                                "method" => "POST",
                                "url" => "http://example.com/redirect.php"
                            ]
                   );







}


elseif  ($keyedInput == 3){
        $response->say('you selected 3');

}
else  { 
        $response->say('Sorry, I don\'t understand that choice.');
        
    }
 
 
 echo $response;


?>

**Redirect.php **


<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;


$response = new VoiceResponse();


$dial = $response->dial('+14151234567',['timeout' => '30']); 

echo $response;

?>



Solution 1:[1]

Twilio developer evangelist here.

Your issue here is that you are using the wrong CallSid to update.

In this case there are two CallSids at play. When your user dials in to your Twilio number, that call between the user and Twilio has one CallSid. Then, when Twilio creates an outbound call to the operator, that call has a different CallSid.

In your application, when you get the <Gather> response from the operator, the CallSid being sent to your endpoint is the CallSid of the operator's call leg. Instead, you need to find the CallSid of the original call.

You should find that the ParentCallSid parameter is sent to the webhook endpoint as well. That ParentCallSid is the original inbound call's Sid and is what you should use to redirect the caller to another operator.

Edit

OK, so I had a go at building this. I don't normally work in PHP, so I wrote this in Node.js (as Twilio Functions). I got it to work and the answer still seems to me to be "use the ParentCallSid, so hopefully it gives you some idea where you might have gone wrong.

The incoming call

This makes an outbound call to my cell phone number, with a url set to make the whisper to me when I answer the call.

exports.handler = function (context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  const dial = twiml.dial();
  dial.number({ url: "/whisper" }, MY_CELL_PHONE_NUMBER);
  callback(null, twiml);
};
<Response>
  <Dial><Number url="/whisper">MY_CELL_PHONE_NUMBER</Number></Dial>
</Response>

The whisper

This returns a <Gather> spoken to the person answering the cell phone number. It offers a choice, dial "1" to connect or "2" to hang up, triggering the original caller to dial to another number.

exports.handler = function (context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  const gather = twiml.gather({ digits: 1, action: "/after-whisper" });
  gather.say("Dial 1 to connect, dial 2 to hang up.");
  callback(null, twiml);
};
<Response>
  <Gather action="/after-whisper" digits="1">
    <Say>Dial 1 to connect, dial 2 to hang up.</Say>
  </Gather>
</Response>

The Gather action "/after-whisper"

This checks the Digits parameter. If it is "1" it immediately calls back with an empty response, signalling the end of the whisper and leading the call to connect with the original caller.

If the Digits parameter is not "1" then we initialise a Twilio client and make a request to the API to update the call with the sid ParentCallSid to a new URL. Once that is complete we return TwiML that says to <Hangup> the whisper call (which wasn't strictly necessary, but I like the intention).

exports.handler = async function (context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  if (event.Digits === "1") {
    callback(null, twiml);
  } else {
    const client = context.getTwilioClient();
    try {
      // Update the parent call with a new URL.
      await client.calls(event.ParentCallSid).update({
        url: URL_TO_NEXT_TWIML,
      });
    } catch (error) {
      console.log(error);
    }
    twiml.hangup();
    callback(null, twiml);
  }
};

The next TwiML

This is the endpoint that the url above relates to, when it gets the callback it just returns a <Dial> to another number, though it could include another whisper and go round this loop again if desired.

exports.handler = async function (context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();

  twiml.dial(NEXT_NUMBER_TO_DIAL);
  callback(null, twiml);
};
<Response>
  <Dial>NEXT_NUMBER_TO_DIAL</Dial>
</Response>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1