Displaying Missed Calls Twilio - twilio

I've been working on a call system with Twilio. Here is how it works:
1.) When someone calls it plays an IVR and they send a response and it redirects them
2.) We then play ringing to them and forward it to a cell phone using DialCallStatus. We use this because we don't want them to hit the voicemail on the cell phone. So, if no one answers the call they hit the twilio voicemail and not the cell phone voicemail. If an operator answers they hear a "press any key to accept the call" they press a key so twilio knows that someone answered the call.
3.) the issue I am having is when I want to display the "missed call" it's not working. I see the call statuses and I tried using the "missed one" but it seems like almost every call is being marked as "completed". Essentially, I want to be able to pinpoint the calls that were not picked up by an actual operator. So, even if they don't leave a voicemail we can call them back.
Sorry for the lengthy question but if you have any idea how to do this it would be much appreciated.

Specific to the, "press any key to accept the call". Are you using the Number noun url parameter with your Dial verb and using a Gather verb returned with the Number URL to collect this positive input? You will receive a Digits parameter if the dialed party presses any key. The Redirect (see below) is the fall through that hits if no digit is pressed (but the call is answered before the dial timeout). I set the value of Digits equal to timeout if no key was entered.
Look at the Twilio Function code below for an example. In my testing, the Dial action URL does correctly show no-answer if the dialed party answers but does not press a digit before the gather DTMF entry timeout.
Initial Dial Number Code:
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.dial({action: "https://x.x.x.x/dialAction"})
.number({url: "https://x.x.x.x/gatherWhisp"}, "+15555551212");
callback(null, twiml);
};
Code than runs when Dialed party answers the call:
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
let digits = event.Digits || null;
if (digits === "timeout") {
// The dialed party never pressed a digit to accept call
// The DialCallStatus returned in the Dial action URL should say no-answer
twiml.hangup();
callback(null, twiml);
} else if (digits) {
twiml.say("You pressed a digit");
callback(null, twiml);
} else {
twiml.gather({numDigits: "1", finishOnKey: ""})
.say("Please press a digit to accept the call");
twiml.redirect("https://x.x.x.x/gatherWhisp?Digits=timeout");
callback(null, twiml);
}
};

Related

Place conference members on hold as they join, then un-hold ALL?

I'm building a Conference Call console app, where my client wants a Start Call button (instead of starting automatically when the moderator enters.) Since there's no API method for starting a conference, I'm thinking the conference will "start" when the first member enters, but then they'll immediately be put on hold. Later, the moderator will press the "Start Conference" button which will un-hold all the participants. I have 2 questions:
Can I add a .then to the dial.conference() that puts that call on hold, or some other way of doing it from that same runtime function? Or do I have to make an api call from my app when I get the callbackstatus that the member has joined?
When I have 175 participants waiting on hold, do I have to send the list of all their CallSids, or is there some way to un-hold all of them?
Thanks...
Can I add a .then to the dial.conference() that puts that call on hold, or some other way of doing it from that same runtime function? Or do I have to make an api call from my app when I get the callbackstatus that the member has joined?
You dial them into the conference via a <Dial> and <Conference> TwiML, see here. You would set the startConferenceOnEnter attribute to false for all of the participants, see here, that puts them "on hold".
When I have 175 participants waiting on hold, do I have to send the list of all their CallSids, or is there some way to un-hold all of them?
You have two options: A) You can have the moderator dial in and set the startConferenceOnEnter attribute to true for them, this would start the conference. B) If you want a button somewhere in your UI you would need to use the Twilio Rest API, specifically the Conference Resource. The status should be in init and you should set it to in-progress. But I'm not sure about that, an alternative would be to get the moderator via the Conference Participant Resource and then set the startConferenceOnEnter attribute to true. You need to experiment a bit.
Here's what I ended up doing. Let's say the Conference is called Capistrano. I put all the participants into Capistrano with startConferenceOnEnter = false. They're happy.
Then, when a moderator calls in, I recognize them by their phone numbers and I put them into a different conference, Capistrano-backstage. They can chat with each other, etc.
Then, when they press the Start Call button, I make an API call that <Dial> them from Capisrano-backstage to Capistrano, with startConferenceOnEnter = true.
Effectively, the conference started when they pushed the button, but really, it started because they entered. Thanks to #philnash for the assist on the code:
// start-conference receives the Confname, Language and the CallSids of all facilitators 'backstage'
// then moves them all to the conference, which starts it.
// This is your new function. To start, set the name and path on the left.
exports.handler = function(context, event, callback) {
switch (event.Language) {
case 'es-MX':
connectnow = 'Ahora lo estare conectando con '+ event.Confname ;
break;
default :
connectnow = 'Now starting ' + event.Confname;
break;
}
const client = context.getTwilioClient();
const twiml = new Twilio.twiml.VoiceResponse();
twiml.say(connectnow, {voice: 'Polly.Kendra', language: event.Language});
const dial = twiml.dial();
// conf-backstage settings for facilitators
dial.conference({
statusCallback: context.STATUSCALLBACKURL,
statusCallbackEvent: 'start join speaking mute leave',
startConferenceOnEnter: true,
endConferenceOnExit: false,
muted: false,
},
event.Confname);
function transfer(CallSid) {
return client.calls(CallSid).update({ twiml: twiml });
}
Promise.all(event.callSid.map(transfer))
.then(() => {
return callback(null, "success");
})
.catch((error) => {
return callback(error);
});
};

Twilio: How to continue the <Dial> Action after callee rejects call

This seems like a really simple issue, but I've searched the web and can't find an answer, unfortunately.
We're trying to setup a basic forwarding/voicemail service on Twilio. Our expected behavior is that a caller will call in and be forwarded to 3 different numbers. If none of the numbers answer, the caller is directed to the company voicemail.
Our issue comes in when any of the numbers reject the call (by pressing the red button on their phone) or are set to Do Not Disturb (which seems to act like a rejection). When this happens, the caller is sent directly to the callee's personal voicemail, instead of the company voicemail. I believe this is because Twilio is passed a "in-progress" status even though the call was rejected.
Is there a way to detect when the call is rejected and continue to the "action" part of our Dial? We looked into the Automatic Machine Detection, but am not sure that is the correct route to take, since it would still reveal the callee's personal voicemail until it determined it was a machine.
Thanks for any help! Our simple code is below.
<Response>
<Dial callerId="+1xxx-xxx-xxxx" timeout="15" action="http://twimlets.com/voicemail?Email=email#email.com&Message=https://website.com/Audio.mp3">
<Number>xxx-xxx-xxxx</Number>
<Number>xxx-xxx-xxxx</Number>
<Number>xxx-xxx-xxxx</Number>
</Dial>
</Response> ```
You can take a look at the blog below, which uses Human Detection (alternatives to AMD) to intelligently handle the dialed party's voicemail.
Outsmart Voice Mail with Human Detection Using Keypress Prompts
You cannot use your current approach with parallel forking, since it cannot account for Voicemail.
You will need to build the sequential dialing logic. Below is some Twilio Function code that may be useful, but you will need to adapt it to meet your needs
Main Function (where you point your Twilio Telephone Number to) is shown first:
Replace:
let phoneTreeUrl = "https://YOUR_FUNTION/PATH";
With your unique Function URL and path (this varies for each Twilio customer). Also update the phoneNumbers to be the phone numbers you want to call. You can pass the Timeout as a variable when you call the function from your Twilio phone number or change it in the code below (it defaults to 30 seconds).
Update the PLACE_HOLDER URL with the other Function you will create in the next step further below.
/*
* Example: Strictly Linear Phone Tree
* This example shows how you might iterate through a list of numbers.
* Each will be tried for 10 seconds before moving on to the next.
*/
exports.handler = function(context, event, callback) {
// REQUIRED - URL to this Function
let phoneTreeUrl = "https://YOUR_FUNTION/PATH";
// Timeout - The amount of time we want to dial before giving up
let timeout = event.Timeout || 30;
// Attempt - The position in the list we are currently at
let attempt = parseInt(event.Attempt) || 0;
console.log(attempt);
// DialCallStatus - Returned by <Dial> action
let DialCallStatus = event.DialCallStatus || null;
console.log(DialCallStatus);
// Phone Numbers - The list of numbers to cycle through
let phoneNumbers = [
"+14073601234", "+16787851234"
];​
// generate the TwiML to tell Twilio how to forward this call
let twiml = new Twilio.twiml.VoiceResponse();
// If we have successfully contacted a number, then exit Function
if (DialCallStatus === "completed" || DialCallStatus === "answered") {
twiml.hangup();
callback(null, twiml);
}
// If we have exhausted all numbers end the attempts
if (attempt >= phoneNumbers.length) {
twiml.say("Sorry, we could not find someone to take your call. Please try again later");
callback(null, twiml);
}
// Build the state for our Twiml response
let target = phoneNumbers[attempt];
let nextAttempt = attempt + 1;
let nextAttemptUrl = phoneTreeUrl + "?Attempt=" + nextAttempt;
twiml.dial({ timeout: timeout, action: nextAttemptUrl})
.number({url: "https://PLACE_HOLDER/humandetect?Detection=required"}, target);
callback(null, twiml);
};
Human Detection Function:
Give this new function the path /humandetect
Update the code below, THIS_FUNCTION, to the unique URL for this FUNCTION (with the path of /humandetect)
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
// REQUIRED - URL to this Function
let phoneTreeUrl = "https://THIS_FUNCTION/humandetect";
let detection = event.Detection || null;
if (detection === "required") {
twiml.gather({timeout: 5, numDigits: 1, action: phoneTreeUrl})
.say("Press any key to hear an important message about your appointment.");
twiml.hangup();
}
callback(null, twiml);
};

How to fix Twilio function to accept incoming calls and have voicemail functionality

When the function is configured on a Twilio phone number the function will immediately direct the calling party to voicemail failing to notify the called party of an incoming call and denying the called party (SIP endpoint) (user#domain.sip.us1.twilio.com) the ability to answer call(s).
I have also tried using TwiML XML. When I configure a TwiML XML on a Twilio phone number, the called party (user#domain.sip.us1.twilio.com) is notified of incoming call(s) and is able to answer calls and will automatically direct calling parties to voicemail when the called party is unavailable. However, when the call ends gracefully the calling party is directed to voicemail which is an issue.
exports.handler = function(context, event, callback) {
let phoneNumber = "sip:user#domain.sip.us1.twilio.com";
const twiml = new Twilio.twiml.VoiceResponse();
if (event.DialCallStatus === "completed") {
twiml.hangup();
} else {
twiml.say("You have reached users voicemail. Please leave a message and I will get back to you.");
twiml.record({
transcribe: true,
transcribeCallback: "http://twimlets.com/voicemail?Email=user#domain.com",
action: "/hangup"
});
}
callback(null, twiml);
};
The function should provide the called party (user#domain.sip.us1.twilio.com) the ability to answer calls and automatically direct the calling party to voicemail when the called party (user#domain.sip.us1.twilio.com) is unavailable. If there is a way to achieve this with a function or TwiML XML that would be great.
Thank you
You can borrow from the Twilio Function code detailed below, it will do exactly what you are looking for (need to check the DialCallStatus to intelligently determine next steps.
Implement Voicemail

Call someone with Twilio then disconnect me and play a message to other person

I'm trying to create a button on a webpage (on my presonal PHP webserver) that should connect me (either call my cellphone or via the webclient), then call a number, I then want to have an options to either hangup the call, or just disconnect me but play an mp3 to the other person and then hangup.
I'm not sure how to go about it. I created a TwiML, but how do I connect that to the existing call? Or is there a different way to do it?
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Play>https://something-something.twil.io/assets/recording1.mp3</Play>
<Hangup/>
</Response>
Thanks in advance.
Twilio developer evangelist here.
This is an ideal use case for Answering Machine Detection. With Twilio's answer machine detection you can set it to Enabled or DetectMessageEnd which means that you can use Twilio to work out whether a machine has answered the call and wait until the message is over then play it a message. Otherwise you can connect the call to yourself.
With PHP, you can generate the call like this:
use Twilio\Rest\Client;
// Your Account Sid and Auth Token from twilio.com/console
$sid = "YOUR_ACCOUNT_SID";
$token = "YOUR_AUTH_TOKEN";
$client = new Client($sid, $token);
$call = $client->calls->create(
"+14155551212", "+14158675309",
array(
"url" => "http://example.com/calls",
"MachineDetection" => "DetectMessageEnd"
)
);
Then, for your URL, you need to respond to the call depending on what the machine detected. You do that with the AnsweredBy parameter. Something like this, which dials your number if someone answers or speaks a message using <Say> if a machine answers:
<?php
if ($_REQUEST['AnsweredBy'] == "human") {
echo "<Response><Dial><Number>YOUR_NUMBER</Number></Dial></Response>";
} else {
echo "<Response><Say>Hello, this is my message</Say></Response>";
}
Let me know if that helps at all.
Edit
Without Answering Machine Detection
Ok, to do this without Answering Machine Detection I recommend you build yourself a dialler using Twilio Client JS. There is a quickstart guide here, so I won't go through how that works here.
Once you have a dialler you can use it to initiate the phone calls. The issue is then moving the voicemail calls to play the message. I would build two buttons, one that hangs up as if you've completed the call successfully and the other that plays the message instead. The first button is a simple function call to Twilio.Device.activeConnection().disconnect().
The second one needs a couple of things. The idea is that it will make a call to your server to redirect the other call to a new set of TwiML.
First up, you need the SID of the call you created. You can get that from the connection object you receive in response to calling connect.
var connection = Twilio.Device.connect({ number: "+1234567890" });
var callSid = connection.parameters.CallSid;
When you want to hangup and play a message you need to send this to your server. This is the SID of the parent call though, and you need to get the child call, the other leg. So, on your server you need to use the REST API to get the other call, then redirect it.
use Twilio\Rest\Client;
// Your Account Sid and Auth Token from twilio.com/console
$sid = "YOUR_ACCOUNT_SID";
$token = "YOUR_AUTH_TOKEN";
$client = new Client($sid, $token);
$calls = $client->calls->read(
array("ParentCallSid" => $_REQUEST['CallSid'])
);
// Loop over the list of calls, it should only have one call in it, and redirect the call to a URL that has the message TwiML
foreach ($calls as $call) {
$call->update(array(
"url" => "http://example.com/message.xml"
));
}
When you redirect the child call, the parent call will no longer be connected so it will hang up. The URL you redirect the child call to should contain the TwiML required to play the message to the machine using <Say> or <Play>.
I think I get what you're trying to do. You have a list of people you're trying to call. The app will call them and connect you. If you hear an answering machine, you want to press a key then hangup and move on to the next call. But after you hang up, that first outbound call stays online and leaves a .mp3 message to that recipient?
I believe one solution would be creating a conference with a bot.
Your app makes an outbound call to you, to the bot and to the recipient and puts everyone into a conference room called "room-timestamp" where timestamp is the current time. The bot is a twilio number that listens for a Gather dtmf. If you press 1, it will play message 1 then hang up. But because this is a conference, you can hangup at anytime and move on to the next call.
The bot could loop a few times and if no dtmf is detected, it will hang itself up.
This is all made easier using the new Outbound conference API where you can pass it the conference name instead of conference SID :
https://www.twilio.com/docs/api/rest/participant#list-post
Edit:
Connect three numbers to a conference room :
$uniqueid = time();
$call = $client->account->calls->create($officeline,$twilionum,
array("url" => "http://yourdomain/conference.php?id=$uniqueid"));
$call = $client->account->calls->create($botline,$twilionum,
array("url" => "http://yourdomain/conference.php?id=$uniqueid"));
$call = $client->account->calls->create($customerline,$twilionum,
array("url" => "http://yourdomain/conference.php?id=$uniqueid"));
This will connect three numbers to a conference room:
$officeline (your number),
$botline (twilio phone # of a bot that responds to dtmf)
$customerline (the customer you're calling)
conference.php just returns a conferenceID for calls to connect to:
header('Content-Type: text/xml');
$confid = $_REQUEST['id'];
echo<<<XMLOUT
<?xml version="1.0" encoding="ISO-8859-1"?>
<Response>
<Dial>
<Conference statusCallbackEvent="leave" statusCallback="killconference.php">$confid</Conference>
</Dial>
</Response>
XMLOUT;
killconference.php is called so that the conference can be terminated when there's only one person left. Just make sure your bot hangs up after playing something.
killconference.php
$theconference = $_REQUEST['ConferenceSid'];
$participants = $client
->conferences($theconference)
->participants
->read();
if (count($participants) == 1) {
$conference = $client
->conferences($theconference)
->fetch();
$conference->update(array(
"Status" => "completed"
));
}
your botline twilio number will be pointing to bot.php that responds to dtmf:
bot.php
header('Content-Type: text/xml');
$dtmf = isset($_REQUEST["Digits"]) ? $_REQUEST["Digits"] : "";
$playmore = "";
if ($dtmf == "1") {
$playmore = "<Say>Hey I just wanted to leave you a message </Say><Hangup/>\n";
}
if ($dtmf == "2") {
$playmore = "<Play>http://www.soundboard.com/mediafiles/22/224470-33a9f640-d998-45a3-b0c1-31c1687c2ae4.mp3</Play><Hangup/>\n";
}
echo<<<XMLOUT
<?xml version="1.0" encoding="ISO-8859-1"?>
<Response>
$playmore
<Gather action="bot.php" numDigits="1" timeout="30">
</Gather>
<Hangup/>
</Response>
XMLOUT;
The bot will stay on the line for 30 seconds, if no dtmf is entered it hangs itself up. Press 1 to leave the customer a message, 2 for Leroy Jenkins

Retrieve Key Input before recording a users call when making calls within Twilio

We have currently developed a phone system for our call centre using Twilio (mainly c#, angular2 and typescript). Our company is currently in the UK but we have now started expanding out to the USA and because of laws in the US it looks like we'd need the ability to choose when we turn on the call recording. In the US people have to consent to recording the call before you can start recording them. I am trying to - when we make an outbound call via twilio to first play a message and get the user to input a key on their dialer to consent to recording the call before continuing with the call. I have attempted to use the gather and say verbs after we have dialled out but this doesn't seem to work. Code below:
`
public override void ApplyAction(TwilioResponse response)
{
var attributes = this.GetAttributes<DialAttributes>("attribute-");
attributes.callerId = this.PhoneNumber;
response.Dial(new Number(this.ReceiverPhoneNumber), attributes);
response.Gather(
new
{
timeout = 10,
finishOnKey = '*'
});
response.Say("We record all calls - please press the star key to consent to call recording");
}`
Twilio developer evangelist here.
The problem is that you are performing the <Dial> before the <Gather>
and <Say>. If you want the user to approve the call first you need to nest the <Say> in the <Gather> and provide an action URL for the <Gather>. When the user presses a button as part of the <Gather> Twilio will make an HTTP request to the action URL with the results in the Digits parameter. Then you should return the <Dial> as the response to that request, if the user signifies agreement. This way you ask them first and then connect the call.
Let me know if that helps.

Resources