Twilio callStatusCallback for conferences not working - twilio

I'm trying to setup a flow where i'm using the twilio client sdk to start a conference.
Frontend creates a device and connects, which in turn calls the route I have setup in the twiml application. Then the following is returned:
$response = new VoiceResponse();
$dial = $response->dial('');
$dial->conference($x->getRoomName(), [
'participantLabel' => 'A participant label',
'startConferenceOnEnter' => 'True',
'endConferenceOnExit' => 'True',
'statusCallbackEvent' => 'start end join leave mute hold',
'statusCallback' => "https://example.com/conference-call/status-callback",
'statusCallbackMethod' => 'POST',
'callStatusCallbackEvent' => 'ringing completed initiated answered',
'callStatusCallback' => "https://example.com/conference-call/call-status-callback",
'callStatusCallbackMethod' => 'POST',
]);
I can get status callbacks for the conference, but not for the call that is actually initiating the conference. As such I am unable to track the call status of the one who is initiating the conference.
Inviting a participant via the API works fine, the callStatusCallback works when creating a participant via the rest API.
Twilio must be creating a participant in the background when we use the Conference term, but there doesn't seem to be a way to subscribe to the call status?
If there is no solution for this, is there a way to use the client sdk to inititate the call, then take care of the business logic only via the rest API? It seems like the twiml is not as powerful as the rest API, but i couldn't find a way to make calls from the browser without a twiml app.

Related

Twilio Conference Call

I was able to implement basic Voice Conferencing but I feel my implementation may be lacking.
client = Client('ACxxxxxxxx', '34xxxxxxxxx')
#app.route('/', methods=["GET", "POST"])
def home():
form = ConferenceList() #form made using flask-wtf
if form.validate_on_submit():
contact_1 = form.data['contact_1'] #callee1
contact_2 = form.data['contact_2'] #callee2
from_ = form.data['from_'] #caller
response = VoiceResponse()
with Dial() as dial:
if from_ == MODERATOR:
dial.conference(
'Conf',
start_conference_on_enter=True,
end_conference_on_exit=True
)
else:
dial.conference('Conf', start_conference_on_enter=False)
response.append(dial)
'''here I feel could be a bottleneck'''
add_user(contact_1, conference_name='Conf', label='laed#1')
add_user(contact_2, conference_name='Conf', label="consumer")
return Response(str(response), 'text/xml')
return render_template('hello.html', form=form)
def add_user(contact, conference_name, label):
participant = client.conferences(conference_name).\
participants.create(
label=label, #label for participant
beep='onEnter',
record=True,
from_='from_', #same as above
to=str(contact)
)
if __name__ == '__main__':
app.run(debug=True, port=8000)
Basically the submit button triggers the / endpoint and the conference starts.
I feel there could be an issue with this implementation as I plan on cleaning it up and pushing to production (salesperson can make a conference call to leads on the app). Is there something I could have done better?
The voice(one client-one client) utilizes the Twilio Voice SDK, it there a way I could tweak it for conferencing?
You only need to respond with TwiML to a webhook from Twilio. In this case it appears that you are responding with TwiML to your application's front end when a submit button is pressed.
So, you can drop all the TwiML:
#app.route('/', methods=["GET", "POST"])
def home():
form = ConferenceList() #form made using flask-wtf
if form.validate_on_submit():
contact_1 = form.data['contact_1'] #callee1
contact_2 = form.data['contact_2'] #callee2
'''here I feel could be a bottleneck'''
add_user(contact_1, conference_name='Conf', label='laed#1')
add_user(contact_2, conference_name='Conf', label="consumer")
return "whatever"
Since the settings you are trying to apply in the TwiML do not apply to the participants joining the conference, you need to adjust your add_user function to use them. In this case, the startConferenceOnEnter and endConferenceOnExit parameters for the moderator should be sent when you create the participant. It might be easier to write two methods, add_user and add_moderator, to make things clear:
def add_user(contact, conference_name, label):
participant = client.conferences(conference_name).\
participants.create(
label=label, #label for participant
beep='onEnter',
record=True,
from_='from_', #same as above
to=str(contact),
start_conference_on_enter=False
)
def add_moderator(contact, conference_name, label):
participant = client.conferences(conference_name).\
participants.create(
label=label,
beep='onEnter',
record=True,
from_='from_',
to=str(contact),
start_conference_on_enter=True,
end_conference_on_exit=True
)
Then call different functions for the different participants:
add_moderator(contact_1, conference_name='Conf', label='laed#1')
add_user(contact_2, conference_name='Conf', label="consumer")
When you call add_user or add_moderator it will make an API request and slow down your server response. If you wanted to offload those requests to a worker, that would make your response quicker. But for 2 API requests, it is likely not a problem.
One other thing you might want to consider is the consumer experience. If they answer the phone before your agent does, then they will be greeted with hold music. You might want to architect it so that the application calls the agent first and only once they have picked up it then dials the consumer. Just worth considering.
Edit
After further explanation, you are now telling me that you want to make the call from the browser using the Twilio Voice SDK for JS.
To make outbound calls with the JS SDK you need to create an access token which includes an outgoing application sid, which refers to a TwiML application. That TwiML application has a voice URL. When you place the call with the SDK, Twilio makes a webhook request to the voice URL of your TwiML app. Your application can perform actions and return TwiML to tell Twilio what to do with the call.
When you create the call with the JS SDK you can pass parameters to the call.
const device = new Device(token);
const call = await device.connect({
params: {
To: ["+15551234567", "+145557654321"]
}
});
Those parameters are sent with the webhook request to your TwiML App voice URL. You can then use a response very similar to your original code to respond here, because you need to return TwiML to the request from Twilio, and start calls to the other participants in the call.
#app.route('/conference', methods=["POST"])
def conference():
const numbers = request.form["To"]
response = VoiceResponse()
with Dial() as dial:
dial.conference(
'Conf',
start_conference_on_enter=True,
end_conference_on_exit=True
)
response.append(dial)
for number in numbers:
add_user(number, conference_name='Conf')
return Response(str(response), 'text/xml')
This code receives the To parameter, a list of numbers to dial into this conference, builds the TwiML response that will put the browser caller into a conference call, places outbound calls to the numbers to dial them into the conference and then returns the TwiML to Twilio. The dialler in the browser will start the conference and the other participants will arrive in the conference when they answer the phone.
In this case you don't make the request to your server yourself, you use the JS SDK to trigger the call and let Twilio make the request to your server. As mentioned in the comments, you may want to offload the API calls to create participants to a background job so that you can respond to the webhook request quicker, but that is beyond the scope of this answer.

How to get a callback when the agent is connected to the customer waiting the queue?

I have a simple setup where there's 1 queue and a few agents that are managed on my side with the help of PHP.
When the customer calls, the call gets enqueued like so:
$response = new VoiceResponse();
$response->enqueue('support', ['waitUrl' => 'wait-music.xml','action' => 'queue-action.php']);
$call = $client->account->calls->create($agent_number, $queue_number, [
"url" => 'queue.php',
]);
echo $response;
queue-action.php, in this case, is only called when the call is ended as it should do. Also, I start a call to an agent to connect him to the queue.
queue-action.php contents:
$response = new VoiceResponse();
$dial = $response->dial('',[
'action' => 'dial-callback.php',
]);
$dial->queue('support');
dial-callback.php here is also triggered after the call is ended.
My goal is to receive a callback when the calls are connected together, so I can mark a certain call as in-progress and assign an agent to it, to later know that this agent is busy on the line.
It feels like I would need to use statusCallbackEvent and statusCallback properties on $dial, but it's only available for <Dial><Number>, <Dial><Sip> and <Dial><Client>.
In other words, I want to record all queued calls in DB and updated their statuses accordingly (initiated / ringing / answered / completed) based on call status updates and assign relations with agents.
Would it be possible to accomplish it somehow using callbacks, without using TaskRouter?
Thank you
I got it working now, don't know how I missed it in the docs but the key is to use
$dial->queue('support', ['url' => 'queue-connect.php']);
the url parameter on Queue just in case someone will read this in the future.

How to make a customable message in Twilio Voice call

i've successfully make a voice call with twilio like this:
$call = $client->calls->create(
$toNumber, $fromNumber,
array("url" => "http://demo.twilio.com/docs/voice.xml")
);
the above code will call to the $toNumber with twilio demo message, when i try to use TwiML to create the dynamic message like this:
$response = new TwiML();
$response->say('Votre otp est 2234', ['voice' => 'woman', 'language' => 'fr']);
$call = $client->calls->create(
$toNumber, $fromNumber,
$response
);
i got an error like this:
[HTTP 400] Unable to create record: Url parameter is required. For
more information, see http://www.twilio.com/docs/api/twiml
Twilio developer evangelist here.
You can't send the TwiML to Twilio when you make a call like that. You need to send a URL that Twilio will send an HTTP request to when the call connects (like in the original example). So, to fix your code you should replace the demo URL with your own URL.
$call = $client->calls->create(
$toNumber, $fromNumber,
array("url" => $YOUR_URL_HERE)
);
That URL can be your own application, or something like a TwiML Bin or Twilio Function. Check out this article to see how to respond with TwiML to a request from Twilio.
I notice that you are building something for one time passwords too. You might be interested in checking out the Authy API that can implement and deliver OTP codes via calls, SMS and app.

Difference between CallTo/To and CallFrom/From in Twilio

When I make an outgoing call using Twilio API & TwiML App Twilio makes request to my server with following params:
'AccountSid' => '...',
'ApplicationSid' => '...',
'Caller' => 'client:2',
'CallStatus' => 'ringing',
'callFrom' => 'some_phone_number',
'Called' => '',
'To' => '',
'callTo' => 'some_phone_numeber',
'CallSid' => '...',
'From' => 'client:2',
'Direction' => 'inbound',
'ApiVersion' => '2010-04-01',
what's the difference between CallTo (which equals the actual phone number I'm calling to) and To (which is empty for some reason) ? Same for From and CallFrom. I couldn't find any info in the docs about callTo/callFrom. Plus, why To is empty?
Update
Some additional info. I make a call from twilio number to real Russian phone number. I actually hangup the call. There is no forwarding/redirecting. The Twiml app for outgoing calls is:
<Response>
<Dial callerId="{{ $callFrom }}"
record="record-from-ringing"
action="{{ $action }}">
<Number>{{ $callTo }}</Number>
</Dial>
</Response>
CallFrom and CallTo equals those which are passed with the request. So the data I mentioned above is what Twilio passes with the request, and then Twilio gets the TwiML instructions.
What I do not understand if that what's the difference between to/from? Are from/to fields used when dealing with client names, like for internal conversation or kind of...?
And another question is: when the call is finished and the action url called, twilio passes DialCallStatus and CallStatus, what's the difference there? I need to store call status so that I know if the client was busy and didn't answer, but DialCallStatus has different value from CallStatus.
Thanks

Need dialer with call transfer after answer

I'm looking for a twimlet or php script for the Twilio API that would implement an outbound calling list function with optional transfer to recorded message -
Sales person Clicks number in a list to dial outbound call
dialed party answers
Sales person realizes it's a machine and clicks a link to transfer the call to a recorded message that will be left on the machine.
Or, if it's not a machine sales person proceeds with the call.
Is something like that available already canned or scripted?
Since the initial ask we've updated the click-to-call solution in a code complete tutorial.
In the last step, after a call is connected and Twilio asks for TwiML instructions you might consider adapting from the call screening tutorial to handle voicemail:
public function agentVoicemail(Request $request, $agentId)
{
$response = new Services_Twilio_Twiml;
$callStatus = $request->input('DialCallStatus');
if ($callStatus !== 'completed') {
$response->say(
'It appears that no agent is available. ' .
'Please leave a message after the beep',
['voice' => 'alice', 'language' => 'en-GB']
);
$response->record(
['maxLength' => '20',
'method' => 'GET',
'action' => route('hangup', [], false),
'transcribeCallback' => route(
'store-recording', ['agent' => $agentId], false
)
]
);
$response->say(
'No recording received. Goodbye',
['voice' => 'alice', 'language' => 'en-GB']
);
$response->hangup();
return $response;
}
return "Ok";
}

Resources