I am following this approach to work on hold and resume of a call from the virtual caller's end. It works but I am facing two more issues. FYI, the setup is exactly same.
First:
The first round of Hold, Resume works perfectly. On Hold, I pass the CallSid, then I apply the <Enqueue> TwiMl to the child calls (only one would be here in my case).
client.calls.list({ parentCallSid: callId }).then(calls => {
calls.forEach(call => {
call
.update({ twiml: '<Response><Enqueue>ADMIN_ID</Enqueue></Response>' });
});
});
And on Resume, I update the Sid of the Caller to Dial again from the queue
client
.calls(callId)
.update({ twiml: '<Response><Dial action="/holding"><Queue>ADMIN_ID</Queue></Dial></Response>' })
It works perfectly foe the first round. When I hit the hold for the second time, it says We are sorry, some application error occurred. Good Bye! at the caller end and disconnects. At the receiver end, it goes to playing hold music. What am I doing wrong?
Second:
I see that setting action in the <Dial> keeps it alive and repeats the action TwiMl in my case. But in a normal scenario without any hold, if the receiver disconnects, I as the caller keeps hearing the action repeatably and the call doesn't hang up. Also while on hold, if the caller or callee hangs up the other one just keeps stuck in the current state. Is there any way to fix this with the above setup?
Fixed the issue by using REST API checks for status of the calls.
client.calls.list({ parentCallSid: callId }).then(calls => {
calls.forEach(call => {
if (call.status !== 'in-progress') {
client
.calls(callId)
.update({ status: 'completed' })
}
});
});
F.e. the above code checks if the parent Sid (virtual caller) is disconnected and in that case it disconnects the child call.
Related
What i'm trying to accomplish:
On an incoming call, I display a small box in the frontend that contains caller info along with a button to pickup the call, and a button to send to voicemail. When the agent clicks the button for voicemail, I want to simply forward the incoming call to voicemail.
The issue:
As long as the call is unanswered and still ringing, I cannot redirect it by updating the call the usual way, it has to be answered to allow updating. If I make the button either ignore or reject the call, I have no specific callback status to track, ignore has no status at all, reject just returns "completed" which is no help to me at all.
What I have tried:
// Called by Ajax on button click, variable $callSid contains the call SID
$client->calls($callSid)->update([
"method" => "POST",
"url" => "https://URL_TO_TWIML"
]);
// TWIML returned by URL_TO_TWIML
$response = new VoiceResponse();
$response->say('Agent is unable to take your call, please leave a message');
$response->record(['timeout' => 10]);
$response->hangup();
return $response;
I have tried to find a usable status on callback for ignore / reject as mentioned previously without success. I've searched far and wide for an answer to this without success.
Maybe i'm looking at it the wrong way, i'm still fairly new to Twilio. Any help would be appreciated.
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:
My question: Can Twilio Announcements be used when there is only a single participant in the conference?
My Test Application: Is a simple node app that keeps a caller waiting while work is done in the background, with periodic updates on progress to the caller, before finally moving the call to a real person.
The problem: I saw Announcements and that this would excellent for my needs.
My node test app successfully dumps each incoming call into its own unique conference. I later attempt to announce an update on the background processing while the caller is waiting.
client.conferences(conferenceSid)
.update({announceUrl:'https://cccbae85.ngrok.io/twilio/announce'})
.then(result => console.log('success'))
.catch(error => console.log('conference error = ' + error));
The following results:
conference error = Error: The requested resource /2010-04-01/Accounts/ACX*/Conferences/CFb15222ac23964077e8161c819cd9dcca.json was not found
When I change this to use a conference AND participant:
client.conferences(conferenceSid)
.participants (particpantCallSid)
.update({announceUrl:'https://cccbae85.ngrok.io/twilio/announce'})
.then(result => console.log('success'))
.catch(error => console.log('conference error = ' + error));
The result is 'success', meaning the conferenceSid was accepted, but I never see any call to the announceUrl (no errors in Twilio, no attempts through ngok).
So back to my question, can Announcements be used in conferences that have a single participant, or do I need to go back to having a "bot" jump into the conference, say something, and jump back out.
OR - did I royally mess up my understanding of Announce....
Thanks!!
I want to use twilio to test our internal phone system, and make sure calls are routing as they should, since our provider is notoriously bad of notifying us to problems.
I'm can initiate a call from twilio, use the "gather" verb to record speech (to ensure we hit the right queue) and then hang up. Everything works fine. Except that the gather ends up taking over 2 minutes to listen to the whole message from our phone system, charging us for 8 15 second gather chunks. I only need the first 15 seconds, but can't figure out how to hangup sooner. Is there a simple way to limit calls to a specific time?
timeLimit, and timeout both don't apply here, since timeLimit only works inside of a dial verb, and timeout only works for pauses in speech during the gather.
Perhaps just set a timer in your code for 15 seconds or so and then use the POST endpoint at /2010-04-01/Accounts/{AccountSid}/Calls/{CallSid} to cancel the call (using the Status=Completed parameter in order to cancel calls even if they are in progress).
If you use their Ruby SDK, and you make a normal call (not a conference call) then you can use the update method:
client = Twilio::REST::Client.new(account_sid, auth_token)
# fetch all in-progress calls between the two numbers
client.calls.list(from: '+11231231234',
to: '+12312311234',
status: 'in-progress').each do |c| #it's supposed to be just one record, but you can play it safe
c.update(status: 'completed')
end
Updating the status to completed should hangup the call if in-progress.
Updating the status to canceled should hangup the call if ringing/queued.
If you know for sure that the call is in-progress and you know the call sid, then you can use:
client = Twilio::REST::Client.new(account_sid, auth_token)
in_progress_call = client.calls(call_sid).fetch
in_progress_call.update(status: 'completed') if in_progress_call.present?
There is some general information in the official docs. Also snippets are available for the other SDKs.
You can find the source code of the update method here for more details.
I'm trying to create a phone system where a caller gets enqueued, and ideally, the system will then call out to an agent, who would then pickup and then modify the call to bridge the top of the queue.
What I've accomplished thus far is the dialing loop, where a user calls in, and it dials agents in sequence, until someone picks up, or gives the user the option to leave a message or stay on the line while hearing it ring. And a simple enqueue with hold music.
I just can't seem to figure out how to combine these two systems.
The closest I've found is this post, and it's helpful, but it glosses over how to call out once the caller is enqueued.
Unfortunately, the only Twilio documentation I've found thus far tells me how to dial into the queue, which isn't what I want out of this system. I want this system to place a caller in a queue with hold music, while the system then dials agent numbers until an agent picks up.
Any and all help is much appreciated.
Thanks.
Edit:
Solution
index.php
This is the general IVR tree that the caller initially hits.
<Say>This hits your general IVR tree</Say>
<Say>As the last action, since the caller hasn't pressed anything and should be enqueued, redirect the caller to EnqueueCaller.php</Say>
<Redirect>./EnqueueCaller.php</Redirect>
Since PHP is a preprocessor, there's no real way to sleep or timeout the dialing of the call. The redirect in the IVR tree is necessary so the Agents aren't being dialed when the user is still in the IVR tree.
EnqueueCaller.php
This is where the Caller gets redirected once the IVR tree has finished and the user has chosen to wait for an agent. The call actually happens before the Enqueue, since PHP loads first before the TwiML xml is read (I think?). But since there's an inherent delay when calling, the caller will always be enqueued before an agent can pick up (I hope).
<Enqueue waitUrl="wait_file.xml">name_of_queue</Enqueue>
$call = $client->account->calls->create($from, $to, "http://example.com/DialQueueHandler.php", array( "StatusCallback" => "DialQueueEventHandler.php" );
DialQueueHandler.php
This simply bridges the agent and whoevers at the top of the queue.
<Say>Connecting to caller now.</Say>
<Dial><Queue>name_of_queue</Queue></Dial>
DialQueueEventHandler.php
This script houses the logic for what happens when the dialed agent state changes (answered, complete, initiated, ringing) from $_REQUEST['CallStatus']. In my case, I dialed a single agent from the enqueue script, and in this script, either continue dialing the next agents via setting of a flag.
switch($_REQUEST['CallStatus'] {
case 'answered':
case 'completed':
$next = false;
break;
default:
$next = true;
break;
}
if($next) { $call = $client->account->calls->create($from, $nextAgentNumber, "http://example.com/DialQueueHandler.php", array( "StatusCallback" => "DialQueueEventHandler.php?agentOffset=$num" ); } //same line from EnqueueCaller.php, and track where we are in agent array.
If the call is not answered or completed, then dial the next agent. Otherwise when the call is picked up by an agent, the DialQueueHandler.php file gets hit and the call becomes bridged.
Jeff, I'm Megan from Twilio.
You can utilize the workflowSid attribute of <Enqueue> to configure a Task which initiates the call flow to an available agent using TaskRouter. There is a TaskRouter quickstart in PHP and I think given where you've gotten so far, you could pick up on the third part.
Let me know if you find this to be helpful.