how to do a Call Forwarding to a IVR? - twilio

I have a ruby application that can make a outbound call
# Sms.multi_call(to: '+155555555555', from: ENV['TWILIO_FROM_PHONE'])
def self.multi_call(to:, from: ENV['TWILIO_FROM_PHONE'] )
twilio_client = Twilio::REST::Client.new(ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN'])
call = twilio_client.calls.create(
url: 'https://sample.com/api/v1/twilio/voice',
to: to,
from: from
)
end
This part is working
When the call is made and goes to the twilio/voice
https://sample.com/api/v1/twilio/voice all its doing is dialing the IVR phone number
<Response>
<Dial>2025555555</Dial>
</Response>
At this point, when I get the phone call and dial an IVR prompt, it auto disconnects.
Any idea how to prevent this?

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 can I capture a dtmf code on for a call sent out of twilio?

I send calls out by building an html file that contains twiml markup, and use the php lib to place the call to the outgoing number (see e.g.)
$tw_call = $twilio_client->calls->create(
"+1".$recipient['address'], "+1".$org['twilio_number'],
array(
'Url' => VOICE_CALL_LINK.'/'.$file, (this contains the SAY verbs and text)
'Timeout' => '30',
'StatusCallback' => CALLBACK_LINK.'/voice_call_handler.php',
'StatusCallbackEvent' => array('initiated', 'ringing', 'answered', 'completed')
)
I want to know if it is possible to record a dtmf code from the call recipient via the method I am using for placing the call?
Can an additional callback url be placed in the text file? If so how would I capture which call the was coming back? Would the call sid be available to the possible callback url within the text file?
Ok I must be missing something. I tried the following:
<Response>
<Pause length='1'/>
<Say voice='alice'>$intro</Say>
<Pause length='1'/>
<Say voice='alice'>$msg_body</Say>
<Pause length='1'/>
<Gather action='absolute html path' numDigits='1'>
<Say Please respond by selecting 1 for I can come. Select 2 for I cannot come.</Say>
</Gather>
</Response>";
I get back from Twilio "an application error has occurred". If I remove the Gather tags and the Say tag within the Gather tags, I receive a perfect call.
Same error occurs if I leave the tags and remove the action and path.
Can you gather responses on outbound calls? I ask because all twilio docs refer to inbound call.
Twilio developer evangelist here.
In order to capture DTMF tones from a call you can use the <Gather> TwiML verb. This would probably go in the file which contains your <Say> that you point to in the code above. <Say> can be nested within <Gather> in order to allow you to ask the user for input and start taking it as soon as they start typing.
The TwiML might look like this:
<Response>
<Gather action="/gather_result.php" numDigits="1">
<Say>Welcome to the free beer hotline, dial 1 for free beer, dial 2 for other beverages.</Say>
</Gather>
</Response>
Then, when the user dials the number (you can control how many numbers with the numDigits attribute) Twilio will make a request to the URL in the action attribute. Within that request will be a Digits parameter which will contain the numbers the user pressed. The call SID would also be among the parameters.
Let me know if that helps at all.
I had similar issue where Gather TwiML was not capturing the user dtmf input from a call sent out of twilio. For some reasons, it failed to capture my input digit. I did press 1#, but the voice message keep playing and repeating the same message. Sometimes it works and twilio able to get the digit that I inputted, but more than 80% of the times I tried, it failed to capture the inputted digit. Below is the TwiML in node js looks like:
var promise = new Parse.Promise();
twilioClient.calls.create({
to: phoneNumber,
from:'+6598124124',
url: hosturl + '/gather_user_dial',
body: callParam,
statusCallback: hosturl + '/callback_user',
statusCallbackMethod: 'POST',
statusCallbackEvent: ["completed", "busy", "no-answer", "canceled", "failed"]
}).then(function(call) {
if (res) res.success(call);
promise.resolve(call);
}, function(error) {
console.error('Call failed! Reason: ' + error.message);
if (res) res.error(error);
promise.reject(error);
});
app.post('/gather_user_dial', (request, response) => {
const twiml = new VoiceResponse();
const gather = twiml.gather({
numDigits: 1,
timeout: 5,
actionOnEmptyResult: true,
action: '/gather',
});
gather.say('You are receiving a call from company A because you press the emergency button. Press 1 if you are okay or Press 9 if you need help, followed by the pound sign.');
twiml.redirect('/gather_user_dial');
response.type('text/xml');
response.send(twiml.toString());
});
app.post('/gather', (request, response) => {
const twiml = new VoiceResponse();
if (request.body.Digits) {
switch (request.body.Digits) {
case '1':
twiml.say('User has been notified!');
userPressOne(request.body.Called);
break;
case '9':
twiml.say('User has been notified!');
userPressNine(request.body.Called);
break;
default:
twiml.say("Sorry, I don't understand that choice.").pause();
twiml.redirect('/gather_user_dial');
break;
}
} else {
twiml.redirect('/gather_user_dial');
}
response.type('text/xml');
response.send(twiml.toString());
});

Using Twilio I want to add a third party into a call using an iOS Device

I'm using Twilio in an iOS app using the TwilioClient library in Swift. The app I've built can connect from user A's device to user B's device or to an outside phone number. This is working fine. What I need to do now is to bring a third party into the call, either a third iOS device (user C) or an additional outside line.
I understand that I will need to use the Conference capability to do this, but I'm not finding examples of how to do this with the TwilioClient library for iOS. Currently my TwiML server is the Python quick start server (which has been fine for two-party calls).
What I'm unclear on is:
Using the TwilioClient device.connect(...) call how do I connect User A & User B into a conference room in the first place?
Once I have A & B in the conference call, how, using TwilioClient, do I bring in party C? I've connected with device.connect(...) which hides all of the REST API code from me.
Since I don't know at the beginning of the call if I'll need the third party is there a way to start off with a direct connect then add all parties to a conference call once we decide we need to add User C?
Thanks,
KeithB
Below is an answer on how to solve question 1 in my OP.
When you use the iOS call device.connect(...) it calls the TwiML server /call URL (in my case a Python server), which returns "TwiML" (Twilio Markup Language). The basic pattern is to call a destination directly. To do this the TwiML server uses the passed parameters To: and From: and constructs a TwiML "Dial" string that is returned. This causes Twilio to dial the To: number using the From: number as a callerid.
In order to have both parties put into a conference it is a two step process. First you modify your TwiML code to tell Twilio to put the Caller into the conference room. Second, you use the REST API at the TwiML server to have Twilio call out to the To: caller and put them in the conference room.
This requires several changes. First you must pass in the conference room name from the device.connect(...) call in your iOS app.
connection = device?.connect(["To": "+1234567890", "Conference": "MyConference"], delegate: delegate)
Then you have to modify the /call route in the Python server to return TwiML for a conference room AND make the outbound REST call.
from twilio.rest import TwilioRestClient
#app.route('/call', methods=['GET', 'POST'])
def call():
account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
auth_token = os.environ.get("AUTH_TOKEN", AUTH_TOKEN)
from_value = request.values.get('From')
to = request.values.get('To')
conf_room = request.values.get('Conference')
callsid_value = request.values.get('CallSid')
# This section creates the TwiML code that will be returned
# This causes the caller to be placed in a conference room
# Note: endConferenceOnExit=true ends conference when call originator exits
conf_response = twilio.twiml.Response()
conf_response.dial(callerId=from_value).conference(conf_room, endConferenceOnExit="true")
# This section makes the REST call out to the To: number
client = TwilioRestClient(account_sid, auth_token)
call = client.calls.create(url="www.YourPythonServer" + "/getconf?Conference=" + conf_room,
to=to,
from_=from_value,
method="POST",
status_callback="www.YourPythonServer" + "/callback?init_callsid=" + callsid_value,
status_callback_method="POST",
status_events=["completed"])
return str(conf_response)
When we call client.calls.create(...) it causes Twilio to then call the URL "www.yourpythonserver/getconf..." to get the TwiML it uses to know how to execute the outgoing call.
#app.route('/getconf', methods=['GET', 'POST'])
def getconf():
resp = twilio.twiml.Response()
conf_room = request.values.get('Conference')
resp.dial().conference(conf_room)
return str(resp)
You'll notice up above we also have a parameter "status_callback". This is used to let us know the To: caller's status. In this case we request 'complete' but we may also get "no-answer", "busy" & "failed". If we don't do this and the call is unsuccessful then the caller will just sit in an empty conference room w/o knowing the outgoing call was unsuccessful. This requires yet another route in our Python server.
#app.route('/callback', methods=['GET', 'POST'])
def callback():
account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
auth_token = os.environ.get("AUTH_TOKEN", AUTH_TOKEN)
init_callsid = request.values.get('init_callsid')
callback_status = request.values.get('CallStatus')
# Check to see what status was passed in. Note that "completed" is
# passed after a successful call and the To: party hangs up or drops.
# You can implement any behavior you want here, but this code
# simply hangs up the originating caller. This might not be appropriate
# if there are still other parties in the call.
if callback_status == 'completed' or callback_status == "no-answer" or callback_status == 'busy' or callback_status == 'failed':
client = TwilioRestClient(account_sid, auth_token)
call = client.calls.hangup(init_callsid)
return str("")
The following is an answer to question #2 in my OP.
After step #1 we have a conference room named 'MyConference' with parties 1 & 2 (From & To). Now we would like to add a third party to the conference and trigger that action from our iPhone.
What we need to do is create a new Route in our python server and call that route directly from our iOS code.
The code in the python server will look something like this:
#app.route('/add_to_conference', methods=['GET', 'POST'])
def add_to_conference():
account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
auth_token = os.environ.get("AUTH_TOKEN", AUTH_TOKEN)
from_value = request.values.get('From')
to = request.values.get('To')
conf_room = request.values.get('Conference')
client = TwilioRestClient(account_sid, auth_token)
call = client.calls.create(url="www.YourPythonServer" + "/getconf?Conference=" + conf_room,
to=to,
from_=from_value,
method="POST",
status_callback="www.YourPythonServer" + "/callback?Conference=" + conf_room,
status_callback_method="POST",
if_machine = "Hangup",
status_events=["completed"]
)
return str("True")
The code above will make a REST call that will cause Twilio to make an outgoing call to the "To" phone number and place the party in the conference room. You will notice that like the code in step #1 we employ the '/getconf' route. The Twilio server will call this URL to get the TwiML that defines how it should process the call.
#app.route('/getconf', methods=['GET', 'POST'])
def getconf():
resp = twilio.twiml.Response()
conf_room = request.values.get('Conference')
resp.dial().conference(conf_room)
return str(resp)
We also defined a '/callback' route that is called upon call completion (hangup) or failure of caller #2 or #3. Since answering Step 1 I have enhanced the way this callback works. My goal is that when all 'To' parties hang up, leaving only the original caller in the conference room, the call should hangup for the original caller. Otherwise, the From caller will just be left in the conference room listening to music. This is not what is expected.
Therefore I implemented the following callback code:
#app.route('/callback', methods=['GET', 'POST'])
def callback():
account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
auth_token = os.environ.get("AUTH_TOKEN", AUTH_TOKEN)
callback_status = request.values.get('CallStatus')
conf_room = request.values.get('Conference')
client = TwilioRestClient(account_sid, auth_token)
conferences = client.conferences.list(friendly_name=conf_room, status = "in-progress")
if (len(conferences) > 0):
conference = conferences[0]
participants = client.participants(conference.sid).list()
if len(participants) == 1:
participant = participants[0]
call = client.calls.hangup(participant.call_sid)
return str("200")
When called, this code grabs the conference room by name, then grabs the list of participants in the conference room. If there is only one participant remaining then it hangs up on the last participant.
The final step is making the call from your iOS code. This is quite simple, you do not need to use the Twilio client libraries for this call, you simply make an http GET request passing in From, To and the Conference room name as parameters. Like this:
http:/yourpythonserver.com/add_to_conference?To=1234567890&From=0987654321&Conference=MyConference
This will activate the entire process and party #3 will be placed in the conference room!

Twimlet that will forward request parameters to next verb

I have this conceptual Twiml that I want the echo twimlet to provide:
<Response>
<Record timeout="10" method="GET" action="http://someURL" />
<Redirect>http://twimlets.com/forward</Redirect>
</Response>
My intent is to use this for outbound dialing, so that the calls that are being made are recorded. The issue is that the request parameters are sent to the echo Twimlet containing the To, From, CallerID, etc. but I really need them passed to the url in the Redirect verb. Is that possible using the echo Twimlet?
Twilio developer evangelist here.
You don't actually need to do this in order to record a two legged call. The <Record> verb is used to record messages, for example a voicemail service.
If you are generating these calls using the REST API, then you can set the call to record in the API call, like this (example in Node.js, I see you've answered some SO questions with Node):
var accountSid = 'AC...';
var authToken = "{{ auth_token }}";
var number1 = '+1555123456';
var number2 = '+1555456789';
var twilioNumber = '+1555654321';
var client = require('twilio')(accountSid, authToken);
client.calls.create({
url: "http://twimlets.com/forward?PhoneNumber=" + encodeURIComponent(number2),
to: outboundNumber,
from: twilioNumber,
record: true
}, function(err, call) {
process.stdout.write(call.sid);
});
You can also give that call a statusCallBack URL that the recording will be POSTed to after the call.
If you are not generating the call from the REST API but you still want to record both sides of the call. You need to use the <Dial> verb and set to record that way. You need to create some TwiML at a URL your Twilio number points to that looks like this:
<Response>
<Dial record="record-from-answer">
{{ onward number }}
</Dial>
</Response>
If you supply an action attribute to the <Dial> verb then once the call is done Twilio will POST the URL of the recording to the action.
I'm not exactly sure how you'd accomplish this with Twimlets. Ideally you want to be able to set the URL that the recording URL is sent to and save it somehow, but you'd need your own server for that. It would be possible to create any of the resulting TwiML you'd need using the echo Twimlet, but it may be better to consider your own server at this point.
Let me know if this helps at all.

Twilio :making a call

I'm not having much luck making a call via Twilio...
I want a button to click while I'm in the office that puts me through to a clients number that I've typed into a web page.
I can do "Click to call": http://www.twilio.com/docs/howto/click-to-call , but that's for the user to phone the company - what I'm after is my company phone phone to call the user.
I thought it would be a simple case of setting the From/To fields in InitiateOutboundCall, and leaving the URL field blank.
It didn't work at all.
I then tried with an empty XML page, because I thought "When the call connects, I don't want the system to do anything else, like saying a message, I just want the two people to talk!
e.g.:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Response> </Response>
When I do that, the client's phone rings, and it says "Company X" on it, but when the phone is picked up it rings off,
I tried with this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Response>
<Dial>
<Number>123456</Number>
</Dial>
</Response>
And it called the client's phone, then called the staff's phone - but that's the click-to-call I mentioned above.
Please help!
Protected sub makeCall()
Dim options As New CallOptions()
Dim ACCOUNT_SID = ""
Dim AUTH_TOKEN = ""
options.Url = xxxxxxxxxxxxxxxxxxxx?
options.From = "+Number listed in Twilio callers page"
options.To = "+My mobile number"
WriteLine(""):WriteLine(""):WriteLine("")
Dim TwilClient As New TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)
WriteLine("TwilClient created.")
Dim outboundCall = TwilClient.InitiateOutboundCall(options)
WriteLine("Call created.")
WriteLine("Call status: " & outboundCall.Status)
If Not IsNothing(outboundCall.RestException )
WriteLine("ERROR!: " & outboundCall.RestException.Message)
End If
IndentLevel +=1
WriteLine("")
WriteLine("SID: " & outboundCall.Sid)
Session("mycallsid") = outboundCall.Sid
Unindent
End Sub
Twilio evangelist here.
Just to clarify, do you want to make a phone call right from within the browser?
If that's the case, you'll need to use Twilio Client, which has a JavaScript SDK that you can use to create an audio connection from your browser to Twilio. Once thats created, you can tell Twilio to connect to a second number and we will bridge the two calls together.
This quickstart shows you how to use Twilio Client with C# to be able to make and receive phone calls from a browser:
http://www.twilio.com/docs/quickstart/csharp/client
Hope that helps.
Devin

Resources