Test Your Webhooks Locally with ngrok

If you’re a Twilio developer, chances are good that you’ve configured a Twilio number to send an HTTP GET or POST to your web app when you receive an incoming call or text.  If you haven’t done that yet, you might want to read this first!  I’ll fire up an NES emulator and play Tecmo Bowl while you check it out.


Ah, back so soon!  I was just about to score my seventh touchdown with Bo Jackson on an inside zone, but I suppose we can get back to business.  As you just read (or already knew) Twilio and other web services use the webhook callback pattern for evented interaction between two systems connected over the Internet (in this case, Twilio and your incredible web application).  This is great when both systems are deployed on the public internet, but what about when you’re developing locally?  How is Twilio supposed to talk to the Ruby on Rails app running on localhost?  Developing on Twilio would be much, much faster if we could connect our phone numbers with the dev environment we run on localhost.

There are a number of services which facilitate exposing your localhost to the internet, but one that we use a lot here at Twilio is ngrok.  ngrok is a free tool that lets you put the web app running on your local machine on the Internet.  Today, I’d like to show you how use it to facilitate local Twilio development.

Step One: Download ngrok

ngrok is a Go program, distributed as a single executable file for all major desktop platforms.  This is super rad – no additional frameworks to install or other dependencies.  Grab the version for your development system of choice and simply unzip the file somewhere on your computer.

Step Two: Write a web application that serves TwiML

The next step is to write a web application that can respond to inbound calls and texts to your Twilio number!  To tell Twilio how to handle an incoming call or message, you need to respond with an XML document that contains TwiML tags.  Any web framework that lets you respond to HTTP requests and render XML will work.

As an example, here’s a tiny Sinatra application, written in Ruby, that will respond to a GET request to “/inbound” with a valid TwiML response:

To run this Ruby application, you will need to have Ruby and the Sinatra gem installed.  On the Mac, you already have Ruby installed.  To install Sinatra, enter the command “gem install sinatra” in a Terminal window to install the Sinatra web framework.  Windows and Linux will require a little more setup.

To start a local HTTP server running this code, open up a Terminal window and create a file called “app.rb”.  Place the code above in this file and save it.  Run the code in your Terminal window with the command “ruby app.rb”.  You should now have a Ruby web server running on local port 4567 – visiting http://localhost:4567/inbound in a web browser should produce an XML document containing the following valid TwiML:

Step Three: Put your app on the interwebs with ngrok

In a Terminal window, navigate to the directory where you unzipped ngrok. Going forward you may want to add ngrok to your system path. That would allow you to access the ngrok command from any directory, but for now we will run the ngrok utility directly from this location.

We now need to start ngrok, telling it which port we want to expose to the public Internet. In the Terminal, type ./ngrok 4567 on Linux and Mac, and just ngrok 4567 on Windows. After starting up, you should see something like the following:

See that URL on the ngrok domain? That’s the brand new home of your local web app on the public Internet. In a browser window, open up https://[your generated ID].ngrok.io/inbound – this should display the same XML found at http://localhost:4567/inbound – verify this is the case.

Now, we can configure our Twilio number to hit our laptop when it receives an inbound call or text.

Step Four: Configure Twilio to work with our local machine

In your Twilio account dashboard, go to your list of incoming numbers and choose one to work with. Enter your ngrok URL, including the “/inbound” route, in the Messaging Request URL field as shown. Also, make sure to change the HTTP request type to a “GET” rather than a “POST”. Save your changes.

Okay, now it’s time to watch the magic happen! Open up the text message app on your phone, and send a text to your Twilio number. If all went according to plan, you should receive a reply from the app running on your local machine!

Next Steps

We’re only scratching the surface of what’s possible with ngrok. Here are some other things you might want to check out:

  • Go to http://localhost:4040 – wait, what!  That’s right my friend, you are looking at detailed request logging for all traffic going through your ngrok URL.
  • If you sign up for an account at ngrok.com, you can reserve a custom subdomain, and configure HTTP basic authentication for access to your tunnel.  Highly recommend you do so.

  • Check out the docs for more uses and information.

  • Pay something for the project, if you can, to support the site and development on it.  Big ups to Shreve for making such an amazing product.

Aside from making local Twilio development a snap, ngrok is great for sharing demos of an in-development website, or maybe capturing inbound HTTP traffic to your API server.  I hope you enjoy using it as much as we do.

  • Mark Headd

    Been trying to develop a Twilio app on my local machine using NGrok but apparently the Twilio platform doesn’t like the latest version (1.6/1.5). I get an error every time I call my app.

    “Error: 11200 HTTP retrieval failure”

    • Anlek

      Were you able to fix this issue?

      • kevinwhinnery

        Hey guys,

        Sorry for the late followup here, but can you provide a bit more information on your setup and where you are seeing this error? I am running the latest on a Mac and am not seeing the issues you describe. If it’s longer than what can fit into this comment thread, send a note to help @ twilio.com

        • Anlek

          I didn’t get the error, I was just preemptively asking :)
          I have it working (amazingly) using 1.6 of NGrok and a Twiml setup.

  • Giorgio Martini

    my sinatra app is running on calhost:4567/home , when i go to the place where i unzipped ngrok and type ngrok 4567 the console seemts to print the right settings:

    Version 1.7/1.6
    Forwarding http://6544378e.ngrok.com ->
    Forwarding https://6544378e.ngrok.com ->

    Web Interface
    # Conn 2
    Avg Conn Time 20.00ms

    but when i go the http://6544378e.ngrok.com, i get a sinatra errror :

    Sinatra doesn’t know this ditty.

    Any ideas ?


    • kevinwhinnery

      That error page/message indicates an HTTP 404 (page not found) error – do you have a route defined for the root url? “get ‘/’ do ‘hello world’ end” or some such? If your app is at /home, you would need to go to http://blahblah.ngrok.com/home

  • Kevin Ng

    I believe everything is working correctly, however I’m not getting the response to my mobile device. Response says 200, with no errors. What am I missing here?

    • Kevin Ng

      never mind I figured it out, I needed to verify my number.

  • JS

    I’m late but… any reason why when i run https://.ngrok.com/inbound in a browser it does NOT take me to anything but google search results?

  • JS

    I’m getting the same Sinatra error “doesn’t know this ditty” … my app.rb file is on the desktop and the Ngrok is unzipped in downloads … any clue what I should do? thanks

  • name

    hahaha. i remember pulling all nighters with tecmo bowl and friends. bo is unstoppable.

  • eggie5

    FYI, it seems ngrok just recently become closed source, so you’ll have to signup w/ them now to use the reverse proxy

    • dontmindme777

      Free plan on ngrok for up to 1 online client, “For quick demos and other simple tunneling needs.”

  • PutsReq can be an interesting substitute for ngrok. Instead of creating a tunnel to localhost, PutsReq will track your requests and responses (handy for inspection/debugging), and allow you to forward them to localhost using its CLI.

  • Tanuj Madhwa

    ngrok generate domain name each time … Do I have to change the callback url in webhook everytime ? Or there is some other way to do it..

    • @tanujmadhwa:disqus With ngrok I use


      Or there is some other way to do it..

      I would also recommend PutsReq with local forwarding: https://github.com/phstc/putsreq#cli