Because we're a marketplace for RV rentals, we need to let customers call rental agencies directly but we also need to be able to track each time a customer calls one of the hundreds of RV rental locations we work with. In other words, we need a proxy phone system.
The finished product will use a single toll-free number and long list of extension numbers. The extension numbers will be generated from the primary key of each row in our agency database model.
When a customer calls in they'll be prompted to enter the extension number (agency ID) and their call will be forwarded on to the RV rental agency. If the customer makes a mistake and enters an invalid extension, we'll forward the call to a general customer service number.
We'll need four routes to handle the various paths calls can take.
namespace :phone do match :gather_extension, via: [:get, :post] match :dial_agency, via: [:get, :post] match :dial_support, via: [:get, :post] match :invalid_extension, via: [:get, :post] end
We also need to buy a number through Twilio and give it a destination URL that is our "gather_extension" route. For us, that url
The XML extension is important; don't forget to include it since Rails won't match .xml routes by default.
Here's the full controller with inline explanations.
class PhoneController < ApplicationController respond_to :xml # Twilio won't be sending the form authenticity tokens so skip them skip_before_filter :verify_authenticity_token def gather_extension # A simple view will prompt the user for the extension number end def dial_agency # The extension number they enter (params['Digits']) is the model ID. # The extension will arrive zero-padded so we have to convert to an integer # Example: extension 0023 will be ID: 23 # Location is the rental agency model that has the agency's destination phone number @location = Location.where(:id => params['Digits'].to_i).where('phone is not null').first # If no matching record is found, redirect to the invalid_extension handler return redirect_to :action => :invalid_extension, :format => :xml unless @location # Log the inbound phone call data. params['From'] is the caller ID number sent by Twilio @location.contacts.create(classification: 'phone', phone: params['From']) end def dial_support # Log the inbound phone call data. params['From'] is the caller ID number sent by Twilio Contact.create(classification: 'phone', phone: params['From'], comments: 'dial_support') end def invalid_extension # Log the inbound phone call data. params['From'] is the caller ID number sent by Twilio Contact.create(classification: 'phone', phone: params['From'], comments: 'invalid_extension') end end
Here's where we gather the extension number from the customer. In our case, we have less than 1,000 agencies
so we can get by with a three-digit extension. Thus we set
:numDigits => 3. Once the customer
has entered three digits, Twilio will send a POST request to
:action => '/phone/dial_agency.xml' with the
entered digits as parameters.
If the customer exceeds the
:timeout => 15 then they'll drop through to
xml.Response do xml.Gather :method => "GET", :timeout => 15, :numDigits => 3, :finishOnKey=>"#", :action => '/phone/dial_agency.xml' do xml.Say 'Hi, thanks for calling RV Menu. Please enter the extension number at any time.', :voice => 'woman' end xml.Redirect '/phone/dial_support.xml' end
The user has entered an extension number and the controller has supplied us with the Location model
that has the agency's phone number so we can just forward the call to the agency using the
xml.Response do xml.Dial do xml.Number @location.phone end end
If the user entered an invalid extension we'll forward to our customer service number and record the call.
xml.Response do xml.Say "That extension doesn't seem to work. Transferring to customer service.", :voice => 'woman' xml.Dial(:record => 'true') do xml.Number '313-485-1949' end end
This view is almost the same as invalid_extension; it gets rendered if the customer exceeds the extension timeout.
xml.Response do xml.Say "Transferring to customer service.", :voice => 'woman' xml.Dial(:record => 'true') do xml.Number '313-485-1949' end end
The last piece of the puzzle is showing the phone number and extension wherever an agency is shown throughout the site. The
only gotcha here is that the model ID number needs to be zero-padded to the right number of digits.
(888) 913-6677 x <%= sprintf('%03d', @location.id) %>
Sayverbs with your own recordings using the
Playverbs for a more professional customer experience.