Brian H
Brian H

Reputation: 334

How can we transfer an outbound Twilio voice call to another number without dropping?

I have read all similar questions here on SO. The developer evangelist has said to use conference calls from the start or look for and modify the child leg of a call.

Conference calls:

Modifying the child leg doesn't work as it seems (as of at least June 2021) that there is no call sid created for the receiving party ("B" here) of a direct call. Modifying the direct call with new TwiML for instance changes the instructions for the caller ("A" here), dropping the receiving party (B) from the call.

Is there a way to accomplish A calls B then A transfers B to C without dropping B and then A hangs up (not dropping B or C)?

Upvotes: 0

Views: 1543

Answers (2)

Sage Gu
Sage Gu

Reputation: 1

in callback from

await twilioClient.calls.create({
    statusCallback,
    statusCallbackMethod: 'POST',
    statusCallbackEvent: CALL_STATUS_EVENTS,
    machineDetection: 'Enable',
    // machineDetection: 'DetectMessageEnd',
    asyncAmd: 'true',
    asyncAmdStatusCallback,
    asyncAmdStatusCallbackMethod: 'POST',
    url,
    to,
    from,
  });

do update

client.calls(event.CallSid ?? '')
      .update({ twiml: `<Response><Dial><Number>${process.env.AGENT1_PHONE_NUMBER}</Number></Dial></Response>` })
      .then(call => {
        console.log('==== update', call.to);
      });

or use FLEX

client.taskrouter.v1.workspaces(workspaceSid)
    .tasks
    .create({
      attributes: JSON.stringify({
        selected_language: 'en',
        direction: 'inbound',
        from: event.Called,
        called: event.Called || 'Unknown', // Include the called number
      }),
      workflowSid: workflowSid,
    })
    .then(task => {
      console.log('Task SID:', task.sid);

      // Update the ongoing call to connect with Flex
      client.calls(ongoingCallSid)
        .update({
          twiml: `<Response><Enqueue workflowSid="${workflowSid}"></Enqueue></Response>`,
        })
        .then(updatedCall => {
          console.log('Ongoing Call updated:', updatedCall.sid);
        })
        .catch(error => {
          console.error('Error updating ongoing call:', error);
        });
    })
    .catch(error => {
      console.error('Error creating TaskRouter task:', error);
    });

Upvotes: 0

philnash
philnash

Reputation: 73090

Twilio developer evangelist here.

I think this question and the comments headed off in a roundabout way that wasn't exactly how to solve the initial problem. Here is what I think the real question is and some options for how to solve it:

Question: In a two party call between participants A and B, where A dialled B, how do I send new TwiML to caller B to redirect them?

In this scenario I am guessing that A has placed the call to B using Twilio Client or the Twilio Voice SDK. And that you have a TwiML application that returns TwiML to <Dial> caller B. To ensure you get the CallSid for caller B's leg you can use <Number> (or <Client> or <Sip>) to provide a statusCallback URL, like so:

<Response>
  <Dial>
    <Number statusCallback="WEBHOOK_URL">CALLER_B_NUMBER</Number>
  </Dial>
</Response>

In the parameters of the webhook request for the status callbac you will receive a CallSid, which identifies the outbound leg of the call to B, as well as a ParentCallSid which identifies the initial leg of the call that A is connected to.

You can then use this CallSid to update the call using the Calls resource and redirect caller B to new TwiML.

As a note, this will drop caller A, unless you provide further TwiML after the <Dial> or in the response to a webhook to the URL action attribute. For example:

<Response>
  <Dial>
    <Number statusCallback="WEBHOOK_URL">CALLER_B_NUMBER</Number>
  </Dial>
  <Say>Caller B either hung up or you successfully transferred them away.</Say>
</Response>

Or

<Response>
  <Dial action="/after-outbound-dial">
    <Number statusCallback="WEBHOOK_URL">CALLER_B_NUMBER</Number>
  </Dial>
</Response>

And then return this in /after-outbound-dial:

<Response>
  <Say>Caller B either hung up or you successfully transferred them away.</Say>
</Response>

The request to the action URL will include a parameter for DialCallStatus to let you know what the outcome of the call was so that you can return different results based on whether it was answered, busy, no-answer, etc.

Alternatives

We discussed in the comments using a conference to perform this action. While a conference is more expensive, it can give a much better experience to the person being transferred. In the case I describe above, transferring caller B will be an abrupt change from talking to caller A to whatever TwiML is next. Using a conference call and dialling a 3rd partner, C, into the call can allow for a warm transfer where caller A can introduce C and also fill in C on what is happening with caller B before dropping.

This does only apply in the case where your transfer is between people. If you need to transfer B to something automated, like a post call survey, then my answer above will suffice. It's just worth considering the conference in case there are warm transfer scenarios you want to explore in the future.

Upvotes: 1

Related Questions