Register by 10/16 for $250 off the on-site price.
Build the future of communications.
Start building for free

Record Bad Customer Service Calls in 30 Lines of Ruby

phoe-recorder

tl;dr If you’d like to record a phone call, three-way call 888.909.1024 or +44 203 389 52 12 into the conversation and you’ll get a text with the recording when you hang up. 

About this time last year, Ryan Block’s “Comcast Call from Hell” made the social network rounds. It was an infuriating eight minute clip of him trying to cancel his service but getting stonewalled by the customer service rep repeatedly asking questions like, “Why is it that you aren’t wanting to have the number one Internet service available?”

Fortunately, Ryan recorded the phone call for, let’s say, “quality assurance purposes.” When it went viral, Comcast stepped in, apologized and made things right.

But what if he hadn’t had that recording?

Next time you find yourself in Ryan’s shoes, ask the rep to hold for a few seconds and three-way call 888.909.1024 (or+44 203 389 52 12 in the UK) into the conversation (and for the sake of staying on the right side of wiretapping laws, tell them that they’re being recorded). When you hang up, you’ll get a text with a link to the recording.

call-recording

This hack lets you record a call on any iPhone or Android device without installing an app, and it was built using only thirty lines of Ruby:

require 'sinatra'
require 'twilio-ruby'

post '/call' do
  response = Twilio::TwiML::Response.new do |r|
    r.Say "This call will be recorded."
    r.Record action: ENV['BASE_URL'] + "/after-record"
  end

  content_type "text/xml"
  response.text
end

def twilio_client
  Twilio::REST::Client.new ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN']
end

post '/after-record' do
  user_phone_number = params['From']
  twilio_phone_number = params['To']
  url = params['RecordingUrl']

  twilio_client.messages.create(
    to: user_phone_number,
    from: twilio_phone_number,
    body: "Here's your recording: #{url}"
  )

 200
end

In the rest of this post, we’ll break down this code in hopes that it can serve as the foundation for your own recording project.

Getting Started

Before we start coding, we’ve got some setup to do.

First, buy a voice and SMS enabled phone number from your Twilio Dashboard (if you’re in the United States, all numbers fit this criteria).

Q1Jd_LSN1ffh6m6sZ_jwZp55e7VgSHUYEyO0G-W5Y2JT-mrLEZynfNqw7jrkVGE3SBneWYyLmkRgbdS18jU3TlRuZAu-mecD64UnSS1uKnkfrZewn_KJPtmSHpVEd0emZPYbj7w

Second, download ngrok which we’ll use to give your local machine a publicly accessible URL (for more details, check out Kevin Whinnery’s brief ngrok tutorial).

Once downloaded, you’ll start ngrok from the command line by passing in the port your server uses. Since Sinatra’s default port is 4567, run it like this (and make sure you leave it running for the rest of this tutorial):

./ngrok 4567

UNaLTwnwbaCUgo_4Cgi8G2BjZcoFygTL2945S1XkDrgdb3kBajrQbVb1uOTiz3w6kACoNiM50W4lzOyixQ294aH4q7cPwhDsPYax-rVOXqZEgC7655VG2bGqBAA0VZV5sON2gk0

ngrok has created a tunnel through which Twilio can send HTTP requests to our local development machine. We’re going to do two things with that ngrok URL:

  • Set it as an environment variable called BASE_URL. There are about eighteen ways to set environment variables for Ruby. I’ll defer to Phil Nash’s excellent post on environment variables to explain those. Here we’ll do it the simplest way by running this in a shell:
BASE_URL=http://YOURNGROKSUBDOMAIN.ngrok.com
  • Paste that URL into the Voice request field on your Twilio number page, then append /call  to it, then save it (we will write the code to handle this request in the next section). Final product should look like this:
  • 6442tg5tqbzCw_YCWdwaUH0Ce4M5Rqhzgst7v-YTESvsx5pXRrgjvtDu7-Y1VSGd2FDIU2A7tr9UkOj0vdnEB0-xi9wG327rkOj-GZLTSVbYqpmQGIUo9sppNKeJ3iYdSPdMII0Third, we need to set up our Ruby project. Create a new directory wherever you keep your code, then change into it:

    mkdir call-recorder
    cd call-recorder
    

    If you use an environment manager like rvm or rbenv (and you should), set that up here:

    echo "2.2.2" > .ruby-version
    echo "call-recorder" > .ruby-gemset
    

    Back out and back into your directory for those settings to take effect:

    cd ../call-recorder
    

    Create a new Gemfile and add Sinatra and the Twilio Ruby gems:

    source 'https://rubygems.org'
    ruby '2.2.2'
    
    gem 'sinatra'
    gem 'twilio-ruby'
    

    Back in the shell, install bundler, then your gems:

    gem install bundler
    bundle install
    

    Create a file called app.rb  and require our two gems at the top of that file:

    require 'sinatra'
    require 'twilio-ruby'
    

    With our plumbing in place, we can now move on to the code to power our call recording.

    Record a Phone Call in Ruby

    When someone calls our Twilio phone number, Twilio makes a POST request to that URL you just copied into your dashboard. Twilio expects a response in the form of Twilio-flavored XML called TwiML. We can use the Twilio Ruby gem to generate this XML instead of writing it out by hand.

    Our TwiML does two things:

    • Greet the caller in a robot voice
    • Record the call

    To generate the TwiML, add this code to the bottom of app.rb:

    post '/call' do 
      response = Twilio::TwiML::Response.new do |r|
        r.Say 'This call will be recorded.'
        r.Record
      end
    
      content_type 'text/xml'
      response.text
    end
    

    Those last two lines set the response headers to return XML and then return the text of the generated response.

    Back in your shell, start your server:

    ruby app.rb
    

    Then call your number and say something worthy of recording. Once you hang up, check out your Twilio recording logs and listen to your message.

    That’s it! Eight lines to record a phone call. Pretty cool, huh?

    Send an SMS in Ruby

    Of course, that recording doesn’t do the caller much good if they have to sign into your Twilio dashboard to play to it. That’s okay though! Recordings are saved at publicly accessible, though highly obfuscated URLs, and it’s easy to text them a link to it.

    To initiate an outbound SMS, we need the account credentials found on our account dashboard:

    CpAcFy41Itc3IFJVxetW6SgvmA6t3rMy-0S-v9TlejKklitFe2z-XHBpmVp2_WR5feW_5U-Uzj8ABB7-vEniJlXu_sFBc0TD9jFRaY8NPKumBE92siY4JAeQYrqUFcwCW9aBgA\

    Kill your Sinatra server, then set these values as environment variables:

    export TWILIO_ACCOUNT_SID=YOURACCOUNTSID
    export TWILIO_AUTH_TOKEN=YOURAUTHTOKEN
    

    Back in app.rb, paste this code to create a method that will instantiate a Twilio REST client using your account credentials:

    def twilio_client
      Twilio::REST::Client.new ENV['TWILIO_ACCOUNT_SID'], ENV['TWILIO_AUTH_TOKEN']
    end
    

    With this client we can connect to Twilio’s REST API and send an SMS. And when do we want to send an SMS? That’s right, when the recording is finished.

    We can add a callback URL to our <Record>  verb that will be called when recording finishes.

    In the post '/call'  method, and replace the r.Record  line with this:

    r.Record action: ENV['BASE_URL'] + '/after-record'
    

    Now when recording is finished, Twilio will make a POST request to the ‘/after-record’ route (which we will soon create). Included in the parameters of that request will be three pieces of information of particular interest to us:

    • params['From']  – The phone number of the caller
    • params['To']  – The Twilio number that was called
    • params['RecordingUrl']  – the publicly accessible URL of the recording

    These parameters match up nicely with the three pieces of information we need to send a text message:

    • What phone number gets the SMS? The number of the person who called.
    • What number sends the SMS? Our Twilio number.
    • What’s in the body of the message? The recording url.

    At the end of app.rb , paste this code to create the /after-call  handler:

    post '/after-record' do
      user_phone_number = params['From']
      twilio_phone_number = params['To']
      url = params['RecordingUrl']
    
      twilio_client.messages.create(
        to: user_phone_number,
        from: twilio_phone_number,
        body: "Here's your recording: #{url}"
      )
    
      200
    end
    

    As for the 200  return value at the end — that’s to be a good web citizen by responding to Twilio’s HTTP request with a success code.

    Restart your Sinatra server, give your phone number a call, say something funny before hanging up, then wait for the text message to roll in.

    Next Steps

    Thirty lines of code and you’ve got a service to protect consumers from the pains of bad customer service. But that’s just the beginning. Building on this foundation, you could:

    • Build a web interface where folks can share their good and bad customer experiences with the rest of the world. They could sign-in using their phone number as a user ID and a PIN sent to them via SMS (it’s super simple to do this using Authy).
    • Create a service for podcasters and journalists to interview folks. You could even turn on the transcription tag to help create show notes. That would look like:
    r.Record action: ENV['BASE_URL'] + "/after-record", transcribe: true
    
  • Use the  verb to build a phone tree (or “IVR” in telecom speak) with custom voicemails for everyone in your company.
  • Whatever you build, I’d love to see it. Let me know at @greggyb or gb@twilio.com.

    Sign up and start building
    Not ready yet? Talk to an expert.