This Ruby on Rails sample application is modeled after a typical call center experience, but with more Reese's Pieces.
Stranded aliens can call an agent and receive instructions on how to get off of Earth safely. In this tutorial, we'll show you the key bits of code that allow an agent to send a caller to voicemail, and later read transcripts and listen to voicemails.
To run this sample app yourself, download the code and follow the instructions on GitHub.
See more IVR application builds on our IVR application page.
Route the call to an agent
When our alien caller chooses a planet, we need to figure out where to route the call. Depending on their input we will route this call to an extension. Extensions are used to look up an agent. Any string can be used to define an extension.
Once we look up the agent, we can use the <Dial> verb to dial the agent's phone number and try to connect the call.
Editor: this is a migrated tutorial. Find the original code at https://github.com/TwilioDevEd/ivr-recording-rails/
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
With this information, we present aliens with a list of available agents so they can pick one. Let's see how we look up an agent.
Look up an agent
When we receive a call from an alien we give them a set of options. In this case the options are:
- For Brodo, press 2
- For Dugobah, press 3
- For Oober, press 4
When our alien caller has made their choice we use the key-press to lookup an Agent.
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
Now that our user has chosen their agent, our next step is to connect the call to that agent.
Call the agent
This code begins the process of transferring the call to our agent.
By passing a url
to the <Number>
noun, we are telling Twilio to make a POST request to the ivr/screen_call
route after the agent has picked up but before connecting the two parties.
Essentially, we are telling Twilio to execute some TwiML that only the agent will hear.
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
Our agent can now be called, but how does our agent interact with this feature? Let's dig into what is happening in the agent's screening call.
The agent screens the call
When our agent picks up the phone, we use a <Gather>
verb to ask them if they want to accept the call.
If the agent responds by entering any digit, the response will be processed by our ivr/agent_screen
route. This will <Say>
a quick message and continue with the original <Dial>
command to connect the two parties.
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
Now our agent can interact with the call, but what if our agent is currently out? In these cases it's helpful to have voicemail set up.
Send the caller to voicemail
When Twilio makes a request to our Call
action method, it will pass a DialCallStatus
argument to tell us the call status. If the status is "completed",
we hang up. Otherwise, we need to <Say>
a quick prompt and then <Record>
a voicemail from the alien caller.
We also specify an action
for <Record>
. This route will be called after the call and recording have finished. The route will say "Goodbye" and then <Hangup>
.
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
Now let's take a step back to see how to actually record the call.
Record the caller
When we tell Twilio to record, we have a few options we can pass to the <Record>
verb.
Here we instruct <Record>
to stop the recording at 20 seconds, to transcribe
the call, and to send the transcription to the agent when it's complete.
Notice we redirect to a URL that is specific to this agent. This is a convenient way to specify which agent was called to produce the voice message. This way we can also save the associated agent together with the voicemail.
class TwilioController < ApplicationController
def index
render plain: "Dial Me."
end
# POST ivr/welcome
def ivr_welcome
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: menu_path) do |gather|
gather.play(url: "https://can-tasty-8188.twil.io/assets/et-phone.mp3", loop: 3)
end
render xml: response.to_s
end
# GET ivr/selection
def menu_selection
user_selection = params[:Digits]
case user_selection
when "1"
output = "To get to your extraction point, get on your bike and go down
the street. Then Left down an alley. Avoid the police cars. Turn left
into an unfinished housing development. Fly over the roadblock. Go
passed the moon. Soon after you will see your mother ship."
twiml_say(output, true)
when "2"
list_planets
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST/GET ivr/planets
def planet_selection
user_selection = params[:Digits]
case user_selection
when "2"
connect_to_extension("Brodo")
when "3"
connect_to_extension("Dugobah")
when "4"
connect_to_extension("113")
else
output = "Returning to the main menu."
twiml_say(output)
end
end
# POST ivr/screen_call
def screen_call
customer_phone_number = params[:From]
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: ivr_agent_screen_path) do |gather|
gather.say(message: "You have an incoming call from an Alien with phone number
#{customer_phone_number.chars.join(",")}.")
gather.say("Press any key to accept.")
end
# will return status no-answer since this is a Number callback
response.say(message: "Sorry, I didn't get your response.")
response.hangup
render xml: response.to_s
end
# POST ivr/agent_screen
def agent_screen
agent_selected = params[:Digits]
if agent_selected
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "Connecting you to the E.T. in distress. All calls are recorded.")
end
render xml: response.to_s
end
# POST ivr/agent_voicemail
def agent_voicemail
status = params[:DialCallStatus] || "completed"
recording = params[:RecordingUrl]
# If the call to the agent was not successful, or there is no recording,
# then record a voicemail
if (status != "completed" || recording.nil? )
response = Twilio::TwiML::VoiceResponse.new
response.say(message: "It appears that planet is unavailable. Please leave a message after the beep.",
voice: 'Polly.Amy', language: 'en-GB')
response.record(finish_on_key: "*", transcribe: true, max_length: '20',
transcribe_callback: "/recordings/create?agent_id=#{params[:agent_id]}")
response.say(message: "I did not receive a recording.", voice: 'Polly.Amy', language: 'en-GB')
# otherwise end the call
else
response = Twilio::TwiML::VoiceResponse.new
response.hangup
end
render xml: response.to_s
end
private
def twiml_say(phrase, exit = false)
# Respond with some TwiML and say something.
# Should we hangup or go back to the main menu?
response = Twilio::TwiML::VoiceResponse.new
response.say(message: phrase, voice: 'Polly.Amy', language: 'en-GB')
if exit
response.say(message: "Thank you for calling the ET Phone Home Service - the
adventurous alien's first choice in intergalactic travel.")
response.hangup
else
response.redirect(welcome_path)
end
render xml: response.to_s
end
def twiml_dial(phone_number)
response = Twilio::TwiML::VoiceResponse.new
response.dial(phone_number)
render xml: response.to_s
end
def list_planets
message = "To call the planet Broh doe As O G, press 2. To call the planet
DuhGo bah, press 3. To call an oober asteroid to your location, press 4. To
go back to the main menu, press the star key."
response = Twilio::TwiML::VoiceResponse.new
response.gather(num_digits: '1', action: planets_path) do |gather|
gather.say(message: message, voice: 'Polly.Amy', language: 'en-GB', loop: 3)
end
render xml: response.to_s
end
def connect_to_extension(extension)
agent = Agent.find_by(extension: extension)
response = Twilio::TwiML::VoiceResponse.new
response.dial(action: ivr_agent_voicemail_path(agent_id: agent.id)) do |dial|
dial.number(agent.phone_number, url: ivr_screen_call_path)
end
render xml: response.to_s
end
end
Legal implications of call recording
If you choose to record voice or video calls, you need to comply with certain laws and regulations, including those regarding obtaining consent to record (such as California’s Invasion of Privacy Act and similar laws in other jurisdictions). Additional information on the legal implications of call recording can be found in the "Legal Considerations with Recording Voice and Video Communications" Help Center article.
Notice: Twilio recommends that you consult with your legal counsel to make sure that you are complying with all applicable laws in connection with communications you record or store using Twilio.
Finally, we will see how to view an agent's voicemail.
View an agent's voicemail
Once we look up the Agent, all we need to do is display their recordings. We bind the agent, along with their recordings, to a View
.
It is possible to look up recordings via the Twilio REST API, but since we have all of the data we need in the transcribeCallback
request, we can easily store it ourselves and save a roundtrip.
require 'twilio-ruby'
class RecordingsController < ApplicationController
# GET /recordings/:agent
def show
agent_number = params[:agent] || "4695186234"
agent = Agent.find_by(phone_number: agent_number)
@recordings = agent.recordings
end
# POST /recordings/create
def create
agent_id = params[:agent_id]
agent = Agent.find(agent_id)
agent.recordings.create(
url: "#{params[:RecordingUrl]}.mp3",
transcription: params[:TranscriptionText],
phone_number: params[:Caller]
)
render status: :ok, plain: "Ok"
end
end
That's it! We've just implemented an IVR with real Agents, call screening and voicemail.
Where to Next?
If you're a Ruby developer working with Twilio, you might want to check out these other tutorials.
Part 1 of this Tutorial: ET Phone Home Service - IVR Phone Trees
Increase your rate of response by automating the workflows that are key to your business.
Send your customers a text message when they have an upcoming appointment - this tutorial shows you how to do it from a background job.
Did this help?
Thanks for checking out this tutorial! If you have any feedback to share with us, we'd love to hear it. Connect with us on Twitter and let us know what you build!