plit banner image with a red Ruby logo and background on the left, and a black section on the right with a white paper airplane and message icon representing communication.

How to Receive SMS Messages with Ruby on Rails

Published on April 24, 2025

Now that your Ruby on Rails app can send SMS and track message delivery with receipts, the next step is handling incoming messages. After all, what’s the point of sending SMS if you can’t receive replies? In this article, we’ll show you exactly how to set that up.

In this tutorial, you will learn how to receive an inbound SMS by implementing a webhook endpoint in Ruby on Rails.

TL;DR Skip ahead and find all the Quickstart code on GitHub.

Prerequisites

To buy a virtual phone number, go to your API dashboard and follow the steps shown below.

  1. Go to your API dashboard

  2. Navigate to BUILD & MANAGE > Numbers > Buy Numbers.

  3. Choose the attributes needed and then click Search

  4. Click the Buy button next to the number you want and validate your purchase

  5. To confirm you have purchased the virtual number, go to the left-hand navigation menu, under BUILD & MANAGE, click Numbers, then Your Numbers

How Does Inbound SMS Work?

You might be wondering how an SMS gets from a user’s phone to your Rails application. Similarly to how delivery receipts worked in the previous post, Vonage handles the interaction with the mobile network. Then, it will forward that information to a webhook that you define in your Vonage application. In this tutorial, we’ll use ngrok to handle the webhook and forward it to our Rails application.

Infographic titled 'Inbound SMS' showing how an SMS is sent from a user, processed through the Vonage platform, and delivered to an application via an API and webhook callback.Diagram illustrating the inbound SMS process, where a user sends a message through the Vonage platform, which then forwards the message to an application via a webhook.

How to Expose a Ruby on Rails App with ngrok

You can use ngrok to safely expose your local server publicly over HTTP. So now, in a separate tab from your rails server, open an ngrok tunnel on port 3000.

ngrok http 3000

Be sure to add your ngrok URL as a config.host in your development.rb file. We’ll use the environment variable VONAGE_SERVER_HOSTNAME, to make our app more dynamic.

For more help, see how to get started and use ngrok in Rails.

# config/environments/development.rb

Rails.application.configure do
   config.hosts << ENV['VONAGE_SERVER_HOSTNAME']

If you haven't created a .env file from before, make sure to do so in the root of your application and add your Vonage credentials and ngrok URL:

VONAGE_APPLICATION_ID='XXXX'
VONAGE_PRIVATE_KEY='./private.key'
VONAGE_SERVER_HOSTNAME='XXXXXX.ngrok.app

>> Keep Your Credentials Secure

>> Never commit .env files to your repository! Make sure .env is in your .gitignore.

Now we’ll need to update our Vonage application (from previous articles) in the developer dashboard. Click on ‘edit’. Now update the Inbound URL under the Messages capabilities. Add your ngrok URL followed by /inbound_sms.

Once you hit save ngrok will now forward requests made to the /inbound_sms endpoint to your Rails application! But there’s one small problem: our Rails application doesn’t have that end point.

How to Handle an Inbound SMS WebHooks in Rails

Now that Vonage is forwarding an SMS payload to the /inbound_sms let’s update our application and create a route to handle it.

# config/routes.rb

Rails.application.routes.draw do
  resources :inbound_sms, only: [:create]
  ....remaining routes...

When an SMS has been sent to your virtual Vonage number, Vonage will notify your application by sending a webhook. A typical payload for an inbound SMS will look something like this.

{
    "to": "12013508504",
    "from": "12013508506",
    "channel": "sms",
    "message_uuid": "a1610e97-a045-4d04-933b-5e570fe51691",
    "timestamp": "2025-02-23T18:21:41Z",
    "usage": {
      "price": "0.0059",
      "currency": "EUR"
    },
    "message_type": "text",
    "text": "Testing",
    "context_status": "none",
    "origin": {
      "network_code": "42507"
    },
    "sms": {
      "num_messages": "1",
      "count_total": "1"
    }
  }

How to Store an SMS Message from Rails

Now that we have all the required information based on our SMS model as previously defined, we can create some logic to store our inbound SMS. We’ll also add a fun feature to show our code is working.  Let’s generate a new controller to handle this.

rails g controller InboundSms create

Inside our controller, we’ll have a simple create method which parses the webhook and enters an SMS into our database. You’ll note that we set is_inbound to true.

# app/controllers/inbound_sms_controller.rb

class InboundSmsController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create

    sms = Sms.create(
      to: params[:to],
      from: params[:from],
      text: params[:text],
      message_uuid: params[:message_uuid],
      is_inbound: true
    )

    # Send a reply
    # reply sms

    # Return an empty HTTP 200 status regardless
    head :ok
  end
end


To prove that we’ve received the sms, we’ll send a reply back to the sender. Let’s have a little fun, showing the original text first without vowels and then in binary.


private

def reply(sms)
    consonants = sms.text.delete("aeiouAEIOU")
    binary = sms.text.unpack1('B*')

    message = Vonage::Messaging::Message.sms(message: "Your message without vowels is #{consonants} and your message in binary is #{binary}")

    vonage.messaging.send(
      from: sms.to,
      to: sms.from,
      **message
    )
end

The reply method relies on the Vonage client, so we’ll need to initialize it like we did in the first article.

 vonage = Vonage::Client.new(
 	application_id: ENV["VONAGE_APPLICATION_ID"],
private_key: ENV["VONAGE_PRIVATE_KEY"]
)

See how all code fits together in the full inbound_sms_controller.rb file.

Conclusion

Congratulations! You’ve successfully built a Ruby on Rails SMS application that can receive inbound messages using a Vonage webhook. And thus you’ve completed the SMS portion of the Ruby on Rails Quickstart. We’ll add voice capabilities with the Vonage Voice API in future tutorials.

If you have any questions or want to request a functionality for the Quickstart application, join the conversation on the Vonage Community Slack or reach out on X (formerly Twitter). We’d love to hear what you build next!

Share:

https://a.storyblok.com/f/270183/384x384/e4e7d1452e/benjamin-aronov.png
Benjamin AronovDeveloper Advocate

Benjamin Aronov is a developer advocate at Vonage. He is a proven community builder with a background in Ruby on Rails. Benjamin enjoys the beaches of Tel Aviv which he calls home. His Tel Aviv base allows him to meet and learn from some of the world's best startup founders. Outside of tech, Benjamin loves traveling the world in search of the perfect pain au chocolat.