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);
});
};
Related
I'm trying to create an interactive dashboard that shows Twilio calls with the ability to listen to any ongoing calls. Is it possible? And what is the best approach I should take?
I saw two methods on the documentation which are Twiml Voice: and Twiml Voice: . Conference doesn't suit with my scenario because incoming calls are not ringing. And I couldn't find a way to listen to voice Streams on the documentation.
Note: Currently inbound calls are handled by Twilio function and WebSocket and outbound calls are handled by Twilio JavaScript SDK.
Thanks in advance.
If you want to listen to a live Voicecall you could try the Stream as IObert suggests. Or, you must use the Conference feature and add yourself as a "Coach." Don't be put off by the name Conference. Think of it as a Voicecall with many more helpful features. You can create a conference from an outbound call like this:
string accountSid = "ACxxxxxx";
string authToken = "xxxxx";
TwilioClient.Init(accountSid, authToken);
var voiceresponse = new VoiceResponse();
var say = new Say("Setting up your conference.");
var dialConference = new Dial();
dialConference.Conference(
name: "Your Conference"
startConferenceOnEnter: true,
endConferenceOnExit: true
);
voiceresponse.Append(say);
voiceresponse.Append(dialConference);
var twiml = voiceresponse.ToString();
var call = CallResource.Create(
twiml: new Twilio.Types.Twiml(twiml),
to: new Twilio.Types.PhoneNumber("E164 Number to Dial"),
from: new Twilio.Types.PhoneNumber("E164 Your Twilio Number")
);
Then once you have a conference started, you can add the second call from either an inbound or outbound call. With an inbound call, use Twiml to send the call to the conference. With an outbound call, use the REST API to add a Conference Participant.
From the client side, using the Twilio Javascript Client, you would then connect your device to the Conference with the correct Conference Twiml parameters which is explained here:
https://www.twilio.com/docs/voice/sdks/javascript/twiliodevice#deviceconnectconnectoptions
const device = new Device(token);
let call = await device.connect({
params: {
To: 'Your Conference'
}
});
I use a slightly different method...I use a button on the client that when you click it makes an outbound call to the "coach" using the Conference Participant REST API. The coach answers and is added with mute = true. The "coach" uses the Twilio Client for WebRTC but the same process will work with a "regular" phone line with a phone number. Using the Twilio Client in this way allows the coach to listen on their computer speakers which is convenient.
The harder part is you will need to keep track of which calls are active on the client side. Otherwise, you won't have an accurate list of which calls or conferences are active. Repeatedly polling the Twilio REST API for active calls is not recommended by Twilio and is super slow compared to using the webhooks to notify of call status changes.
I'm using an incoming call flow that starts call recording, asks a bunch of questions, gathers responses, at the end of the call: stops recording and sends a sms to caller using send/wait for reply widget. A response is expected and based on what's in the body of the incoming call, it calls a function.
All this works, except, I am not receiving a response back from the caller.
My concurrent call setting =off
incoming trigger = incoming call
the flow is tied to a phone number (voice)
I'm not sure how to get a reply back into the same flow. Do I need to attach something to the message section of the phone number?
Any guidance would be appreciated
A Studio flow execution represents one call, one SMS, or the incoming call trigger from the REST Trigger. As the call initiates your flow, it will terminate when the call ends.
But you can work around this by using a function that gets invoked when the recording is done. This function can then use the Twilio APIs to fetch contextual information from the call and trigger the REST API interface of the same flow (but with a different trigger).
I created a small example that does something similar:
The flow is triggered by a call, starts a recording, and gathers data
There is a recording callback URL that points to my function
// This is your new function. To start, set the name and path on the left.
exports.handler = function (context, event, callback) {
console.log(`Recording ${event.RecordingSid} state changed to ${event.RecordingStatus}.`)
if (event.RecordingStatus === "completed") {
const client = context.getTwilioClient();
return client.calls(event.CallSid).fetch()
.then(call => {
client.studio.v2.flows(<Flow ID>)
.executions
.create({
to: call.from,
from: <YOUR NUMBER>,
parameters: {
RecordingUrl: event.RecordingUrl,
}
})
.then(execution => {
console.log(`Triggered execution ${execution.sid}`)
return callback(null, "OK");
});
})
.catch(callback)
}
return callback(null, "OK");
};
You can find the ID of your flow in the console (or when you click on the root element and check the Flow Configuration):
The REST API triggers a second flow execution that reads the parameter and uses them to send a text message:
I have successfully implemented Voice calls functionality in my php application using JS SDK.
Now, I need to implement Call Monitoring and barge in features, that I believe are only available using Twilio Conference.
My current code looks like
$response = new VoiceResponse();
$dial = $response->dial('');
//If Incoming call, redirect the call to my browser client
if($phonenumber == $mytwiliophonenumber)
{
$dial->client($browserclientname);
}
//If outgoing, make the call to the phone number
else
{
$dial->number($phonenumber);
}
Now, what is the easiest way to change this to conference?
I have read that I need to dial the conference, but it is not working.
$dial->conference('anyconferencename');
Any guidance?
A conference has a fundamental difference to connecting two callers together in a regular dial. A conference acts as a room that participants join individually, rather than when you use client or number which places an outbound call leg to the client or number you are dialling.
If you have your user dial into a conference using $dial->conference you will need to create another leg to call the other person into the conference too. You can do this using the conference participants API.
So, instead of your current code, you could update to something like this:
$response = new VoiceResponse();
$dial = $response->dial('');
//If Incoming call, redirect the call to my browser client
if($phonenumber == $mytwiliophonenumber)
{
$participant = "client:" . $browserclientname;
}
//If outgoing, make the call to the phone number
else
{
$participant = $phonenumber;
}
$conferenceName = 'conferencename';
$twilio->conferences($conferenceName)
->participants
->create($mytwiliophonenumber, // from
$participant, // to
);
$dial->conference($conferenceName);
In this option, regardless of whether the call is inbound or outbound, the caller is placed in a conference call. And another call is generated to add the other participant to the conference call too.
I am using Twilio to call my customers from my Twilio client and want to put the user on hold whenever is needed. For that i am calling RESTAPI whenever user clicks on the "Hold" Button.
But after calling the method call is getting disconnected for My Twilio client and playing Hold sound for my customer. Can you please suggest something for this. Both the below approaches are not working
var response = new VoiceResponse();
var dial = new Dial();
dial.Conference("Customer Waiting Room", beep: Conference.BeepEnum.False);
response.Append(dial);
var call = Twilio.Rest.Api.V2010.Account.CallResource.Read(parentCallSid: callSid).ToList();
Twilio.Rest.Api.V2010.Account.CallResource.Update(new Twilio.Rest.Api.V2010.Account.UpdateCallOptions(call[0].Sid) { Twiml = response.ToString() });
return Content(response.ToString(), "application/xml");
Alternative:
var response = new VoiceResponse();
response.Say("You have a caller on hold.");
var call = Twilio.Rest.Api.V2010.Account.CallResource.Read(parentCallSid: callSid).ToList();
response.Enqueue("admin");
Twilio.Rest.Api.V2010.Account.CallResource.Update(new Twilio.Rest.Api.V2010.Account.UpdateCallOptions(call[0].ParentCallSid) { Twiml = response.ToString() });
Twilio developer evangelist here.
You have a direct call setup between your agent and you customer. When you move your customer call away from that, then your agent call will move on to the next TwiML instruction. If there are no further TwiML instructions available for your agent, then they will get disconnected.
To overcome this, you need to consider what you want your agent to do while your customer is on hold. It might be better, for example, to have your call take place in a conference. That way, when you put your customer on hold, the agent can remain in the conference.
Otherwise, you should provide further TwiML instructions after your agent's <Dial> so that they continue the call after the customer leg goes away.
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);
};