Prompt agent for acceptance before dequeuing an incoming call - node.js

I've set up a basic workflow on Twilio/node.js, that will transfer an incoming call to agents' phones. Because the agents might have a voicemail setup on their phone/cell phone, I wanted to prompt them for acceptance through a "gather" instruction. If they press 1, then the call will be transferred ("dequeue" instruction). Otherwise, the reservation is to be rejected.
My problem is that I don't know how to order the instructions: if I gather before enqueuing, then gather doesn't seem to know yet "who to ask". But if enqueue before gathering, then the prompt message will be heard by the caller (incoming client) and it will be too late to reject the reservation...

Twilio developer evangelist here.
In this case you should not respond to the assignment callback with "dequeue". Instead, you should use the "call" instruction to initiate a call to the agent instead.
With the call instruction you include a URL that will be requested when the call connects to the agent. At this point you should read out your message and use <Gather> to ensure the agent is ready for the call. When the agent presses a button or the <Gather> times out, the action attribute of the <Gather> will be requested. Then, if you find the user did enter a digit, by checking the Digits parameter you can respond with TwiML to <Dial> the <Queue>. In order to dial the actual caller that the reservation refers to, you need to add the reservationSid attribute to the <Queue> element. You will receive the ReservationSid as a parameter in the task assignment webhook. When the call bridges it will accept the reservation.
If the <Gather> times out, then you should just reject the reservation and it will be passed to the next worker.
Let me know if that helps at all.
Edit
Example TwiML for this situation.
You first respond to the assignment callback with JSON, using a call instruction:
{
"instruction": "call",
"to": AGENT_PHONE_NUMBER,
"from": YOUR_TWILIO_NUMBER,
"url": "https://example.com/worker_call?ReservationSid=${req.body.ReservationSid}&TaskSid=${req.body.TaskSid}"
}
Note, I have added the ReservationSid and TaskSid from the incoming request to the URL of the webhook that is called when the worker answers their phone.
Then when the worker answers their phone Twilio calls to the url we set above and we use it to ask them if they want to take the call.
# /worker_call
<Response>
<Gather action="/gather_result?ReservationSid=${req.query.ReservationSid}&TaskSid=${req.query.TaskSid}" numDigits="1">
<Say voice="alice">You have an incoming call, dial any number to accept</Say>
</Gather>
<Redirect>/gather_result?ReservationSid=${req.query.ReservationSid}&TaskSid=${req.query.TaskSid}</Redirect>
</Response>
Note, I am passing the ReservationSid and TaskSid along from the incoming request URL to the URL of the <Gather> action. Now, when the action URL is called we need to respond based on whether the call was accepted or not. I need to use some actual code for this rather than just TwiML, so I'll do it with Node. We need to be able to call the reservations resource in the REST API, so this comes with some setup:
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const workspaceSid = 'WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const twilio = require('twilio')
const client = twilio(accountSid, authToken);
app.post('/gather_result', function(req, res) {
const twiml = new twilio.twiml.VoiceResponse();
if (req.body.Digits && req.body.Digits.length > 0) {
// There was a positive result, call the Queue and connect the caller
const dial = twiml.dial();
dial.queue({queue: req.query.ReservationSid});
res.send(twiml.toString());
} else {
// no response, reject the reservation.
client.taskrouter.v1
.workspaces(workspaceSid)
.tasks(req.query.TaskSid)
.reservations(req.query.ReservationSid)
.update({ reservationStatus: 'rejected' })
.then(function() {
// once the reservation is updated, then hangup the call.
twiml.hangup();
res.send(twiml.toString());
});
}
});

Related

How do I automatically cancel Twilio outbound call as soon as the call status changes to ringing

hello there I am using Twilio to place outbound call I have local node app that initiates a call. I also have Twilio status callback function which monitors the status of call. I want to cancel/end the call when the call status changes to "ringing" I have tried to end a call with hangup and reject TwiML. I was expecting a call to be cancelled automatically but my phone keeps ringing.
node code for placing a call
router.post('/call', async (requ, resp) => {
const accountSid = myaccoundsid;
const authToken = myauthtoken;
const client = require('twilio')(accountSid, authToken);
client.calls
.create({
to: 'to_number',
from: 'from_number',
url: 'http://demo.twilio.com/docs/voice.xml',
statusCallback: 'url_to_my_status_call_back_function',
statusCallbackMethod: 'POST',
statusCallbackEvent: ['initiated', 'ringing', 'answered', 'completed'],
})
.then((call) => {
console.log(call.status);
});
});
The call status returned in my terminal is queued.
My status callback function
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const response = new VoiceResponse();
exports.handler = function(context, event, callback) {
if(event.CallStatus ==='ringing'){
console.log("Call status changed: "+ event.CallStatus);
response.hangup();
console.log(response.toString());
}
callback(null, response);
};
Twilio console
any help will be appreciated thank you
Twilio developer evangelist here.
First, you should likely check with the Twilio terms, specifically the terms related to voice calls to ensure that you do not fall foul of this rule:
Customer will not have, in a given month, (a) a high volume of unanswered outbound voice call attempts from a single originating phone number or (b) a low average outbound voice call duration. Telecommunications providers may independently block Customer’s use of their networks if Customer engages in any of the foregoing behavior.
If you have a legitimate reason to hang up a call as soon as it starts ringing, then read on.
Status callback webhooks are asynchronous, they happen outside of the call itself, so returning TwiML to them does not affect the call. Instead, to use a status callback to affect a call, you need to use the REST API to modify the call and complete it.
An example of this might be:
exports.handler = async function(context, event, callback) {
if(event.CallStatus ==='ringing'){
const client = context.getTwilioClient();
await client.calls(event.CallSid).update({ status: 'canceled' });
}
callback(null, response);
};

How to use twilio and twiml to record a call and redirect the call or join someone else to the call

Hi im trying with node to receive a call, start recording the call and join someone else to the call (also tried redirect) and maintain the call recording. all this have been without success.
i tried
twiml.say('HI, Your Calling XXXXX, ');
twiml.dial("XXXXXXX");
twiml.record();
but when the dial command is used the record command doesn't work, if i comment the dial command, the call get recorded.
pleas hope you can help me find how i can record and redirect a call and while keep recording it
Twilio developer evangelist here.
Using <Record> only records one leg of a call and is mostly used for voicemail style operations.
To record inbound calls with Twilio you need to use the Call Recordings API to trigger the recording to start.
Note, you need to have upgraded your account to trigger recordings like this.
You can trigger this API call as soon as you receive the inbound webhook. You can see the documentation for the call recording resource here. If you were writing this in Node.js using Express, your route might look like this:
// Load your Account Sid and Auth Token somehow, for example, from the environment
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const twilio = require('twilio')
const client = twilio(accountSid, authToken);
app.post('/calls', (req, res) => {
const callSid = req.body.CallSid;
client.calls(callSid)
.recordings
.create()
.then(recording => console.log('Recording started')
.catch(err => console.error('Recording failed. ', err);
const twiml = new twilio.twiml.VoiceResponse();
const numberToDial = process.env.NUMBER_TO_DIAL;
twiml.say(`Hi, you're calling ${numberToDial}.`);
twiml.dial(numberToDial);
res.set('Content-Type', 'text/xml');
res.send(twiml.toString());
});
Let me know if that helps at all.

'Until loop' analogue needed - in order to continue bot dialog - after some status 'marker' is updated

'Until loop' analogue needed to continuously read status variable from helper function - and then (when the status variable is 'as we need it') - to resume bot conversation flow.
In my bot (botbuilder v.3.15) I did the following:
During one of my dialogues I needed to open external url in order
to collect some information from the user through that url.
After that I posted collected data (with conversation ID and other info) from that url to my bot app.js
file
After that I needed to resume my bot conversation
For that I created helper file - helper.js in which 'marker' variable is 'undefined' when the data from url is not yet collected, and 'marker' variable is some 'string' when the data is collected and we can continue our bot conversation
helper.js
var marker;
module.exports = {
checkAddressStatus: function() {
return marker;
},
saveAddressStatus: function(options) {
marker = options.conversation.id;
}
}
I can successfully update variable 'marker' with my data, by calling saveAddressStatus function from app.js.
However, when I get back to writing my code which is related to bot conversation flow (To the place in code after which I opened url - in file address.js, and from where I plan to continuously check the 'marker' variable whether it is already updated - in order to fire 'next()' command and continue with session.endDialogWithResult -> and then -> to further bot conversation flows - I cannot find the equivalent of 'until loop' in Node.js to resume my session in bot dialog - by returning 'next()' and continuing with the bot flow.
address.js
...
lib.dialog('/', [
function (session, args, next) {
...
next();
},
function (session, results, next) {
// Herocard with a link to external url
// My stupid infinite loop code, I tried various options, with promises etc., but it's all not working as I expect it
while (typeof helper.checkAddressStatus() == 'undefined') {
console.log('Undefined marker in address.js while loop')
}
var markerAddress = helper.checkAddressStatus();
console.log(markerAddress);
next(); // THE MOST IMPORTANT PART OF THE CODE - IF markerAddress is not 'undefined' - make another step in dialog flow to end dialog with result
function(session, results) {
...session.endDialogWithResult({markerAddress: markerAddress})
}
...
Any ideas how to make a very simple 'until loop' analoque in this context - work?
Having your bot stop and wait for a response is considered bad practice. If all of your bot instances are stuck waiting for the user to fill out the external form, your app won't be able to process incoming requests. I would at least recommend adding a timeout if you decide to pursue that route.
Instead of triggering your helper class in the endpoint you created, you should send a proactive message to the user to continue the conversation. To do this, you will need to get the conversation reference from the session and encode it in the URL that you send to the user. You can get the conversation reference from the session - session.message.address - and at the very least you will need to encode the bot id, conversation id, and the serviceUrl in the URL. Then when you send the data collected from the user back to the bot, include the conversation reference details for the proactive message. Finally, when your bot receives the data, recreate the conversation reference and send the proactive message to the user.
Here is how your conversation reference should be structured:
const conversationReference = {
bot: {id: req.body.botId },
conversation: {id: req.body.conversationId},
serviceUrl: req.body.serviceUrl
};
Here is an example of sending a proactive message:
function sendProactiveMessage(conversationReference ) {
var msg = new builder.Message().address(conversationReference );
msg.text('Hello, this is a notification');
msg.textLocale('en-US');
bot.send(msg);
}
For more information about sending proactive messages, checkout these samples and this documentation on proactive messages.
Hope this helps!

How to get Conference Sid at the time of dialing twilio call

I've been working with twilio, using Node.js, and dialing call between two web end points. One is client and other is agent. I'm using following code to dial call.
function dialCall(calledNumber, url) {
client.calls.create({
to: `client:${calledNumber}`,
from: twilioNumber,
url: url
})
.then(call => call.sid));
}
I'm using following twiml to establish a call.
const generateTwiml = (conferenceName) => {
let twimlResponse = new VoiceResponse();
twimlResponse.say(`Welcome to unity dialer.`, {
voice: 'alice',
});
const dial = twimlResponse.dial({
timeLimit: '600',
});
dial.conference({
startConferenceOnEnter: true,
endConferenceOnExit: true
}, "Test Room");
return twimlResponse.toString();
};
I've been successfully calling both agents and clients and getting callSid of both calls. However, my question is that at this point of time I also want to get conference Sid as well as I'm dialing the call as conference. What is the method to get that. As per documentation there is a method to fetch conference using conference name and status. However, if I use this some time the same is not returned due to race condition and I have to implement set time out function for same arbitrary delay. I've been getting the result but is there any other solution available for that.
Twilio developer evangelist here.
At the time you return the TwiML to create the conference there is not yet a conference resource so there's no way to get the conference SID at that stage.
As you describe, you can use the conference resource to list conferences and filter by the name you give it. However, you can't list the conferences at the time you return the TwiML because that conference hasn't been created by then.
Rather than setting a timeout, which could be flaky, I recommend you use the statusCallback attribute of the <Conference> TwiML to set a URL to callback to when the conference starts. In the parameters to that callback you will get the ConferenceSid.

How can I get the caller from an incoming phone call webhook request?

I'm going to develop a webhook for incoming calls in Twilio:
app.post('/voice', (req, res) => {
const twiml = new VoiceResponse();
const caller = ...;
twiml.say('hello, your number is ' + caller);
res.type('text/xml');
res.send(twiml.toString());
});
How can I get the caller phone number from the request (req)? I don't need the name, but just the number.
I can't find in docs what is sent in the POST body when a webhook is invoked.
The caller phone number you're looking for is in req object.
It's req.body.From.
In your example const caller = ...;
becomes const caller = req.body.From;.
Docs:
Request parameters
https://www.twilio.com/docs/api/twiml/twilio_request#synchronous-request-parameters
Respond to incoming phone calls in Node.js
https://www.twilio.com/docs/guides/how-to-respond-to-incoming-phone-calls-in-node-js#write-nodejs-code-to-handle-the-incoming-phone-call
Call Monitoring with Node.js
https://www.twilio.com/blog/2015/09/monitoring-call-progress-events-with-node-js-and-express.html

Resources