How to write an Intercom Messenger App with Ruby on Rails

Illustration of a concentrated man sculpting a clay figure with a small statue of Michelangelo's David to his left. Illustration of a concentrated man sculpting a clay figure with a small statue of Michelangelo's David to his left.

Intercom used to be at the heart of all our sales and customer support communications. We were happy users of it's Early Stage program and we are still happy to use it after we became large enough to pay the full price.

One of the things we really like about Intercom is how it integrates with all of our existing tools. We can see payment date from Stripe right inside Messenger interface, for example. We also took effort to integrate it with our events pipeline, so that we can see all the relevant user events in Intercom and do basic automation based on these events. In general, we love tools that solve particular problem for us but at the same time allow us high level of customization and integration.

In April 2018 Intercom launched the new Messenger with the key feature being the addition of Messenger Apps - various widgets right inside Messenger window. That could be newsletter subscription form, Product Demo request or just showing some simple link to the featured content on your website. The last app is called Showcase and it allows you to pick and image, a title, a text and a link to which user is taken by clicking on all of this.

It looks like this:

Mentorship was quite a unique product, and sometimes we have lengthy conversations with potential customers about how exactly mentorship process looks like with us and our mentors. To reduce amount of work our Intercom agents have - and, of course, to try out the new Messenger features - we added both Mentorship and Pricing pages as a Showcase app in the Intercom.

Then we instantly hit the limitation of the built-in Showcase app: it doesn't support multiple languages and we offer mentors both for English and Russian speaking audiences. Luckily, Intercom provides the whole platform with API to build your own Messenger applications and these could be as complex as you want.

There was no requirement for anything really complex, we only needed Intercom to show different Showcase blocks depending on the user's preferred language. So we wrote our first Intercom App.

I am not going to bore you with re-citing the excellent documentation of Intercom on the topic. Instead, I will use it as a base to show you how exactly to build a complete basic Messenger App by using Ruby on Rails as a backend. If you are not using Ruby on Rails, then this guide will be of little use to you.

Create a new workspace and configure it

Find the Developer Hub of Intercom, create new application and configure the URLs to different flows.

"What are these flows?", you might ask.

Messenger App Lifecycle

Messenger App goes through different Lifecycle Flows starting with the Configure Flow.

Configure Flow is what happens when you are adding the application to the Messenger in the Intercom app. It allows you to pass configuration options to your app:

Once application is configured and set live, you can use it by opening your website and opening the Intercom Messenger. When it loads, Initialize Flow happens together with the magic - Intercom makes a request to the backend of the application (written by you), application parses the request and returns "something" back, allowing Intercom to render the application accordingly.

As our goal is a simple Showcase app with multiple languages support, we are not going to build any specific Configuration flow and just focus on Initialize Flow as it's responsible for initial rendering of the app.

When you create a new app you have to specify URLs for each of the flows except Configure - Intercom needs to know where your app lives. But we will get back to it in a moment, first let's prepare the development environment.

Preparing development environment

To simplify your tests you need to setup Intercom to show up in your development environment. To do this you need to add Intercom libraries to the Gemfile:

gem 'intercom-rails'
gem 'intercom'

Run bundle install and configure API access in config/initializers/intercom.rb:

IntercomRails.config do |config|
  config.app_id = Rails.application.credentials.intercom_app_id
  config.api_secret = Rails.application.credentials.intercom_app_secret
end

IntercomClient = Intercom::Client.new(token: Rails.application.credentials.intercom_access_token)

Here we are using both intercom gem to access the API directly and intercom-rails to enable Messenger for the application. If you already use Intercom then you probably have done it already.

Now make sure Messenger is actually attached to your application layout. This is what we have in our base layout which is used for every application page:

<% if Rails.application.credentials.intercom_app_id.present? %>
    <% if current_user %>
      <%= intercom_script_tag({
        user_id: current_user.id,
        email: current_user.email,
        name: current_user.fullname,
        created_at: current_user.created_at,
        language_override: I18n.locale.to_s,
        hide_default_launcher: current_user.has_role?(:mentor),
        mkdev_url: admin_user_url(current_user)
      }) %>
    <% else %>
      <%= intercom_script_tag({
        language_override: I18n.locale.to_s
      }) %>
    <% end %>
<% end %>

Make sure Rails.application.credentials.intercom_app_id and Rails.application.credentials.intercom_app_secret are the ones for test/dev workspace of Intercom and not the one that your actual users will see.

Once you start the server and navigate to localhost:3000 in your browser you should see Intercom icon pop up at the right bottom of your screen. Now you are ready to test!

Exposing the app to the Intercom

First let's provide a few endpoints for Intercom to send data to. Create a simple IntercomController with two methods - init and submit. At mkdev we nest this kind of simple integrations controllers in an Integrations namespace:

class Integrations::IntercomController < Integrations::BaseController
  protect_from_forgery with: :null_session
  skip_before_action :verify_authenticity_token

  respond_to :json

  def init
  # Initialize Flow happens here
  end

  def submit
  # Submit Flow happens here
  end

end

Add a route for these:

  namespace :integrations do
    scope path: "intercom", as: :intercom do
      post "init" => "intercom#init"
      post "submit" => "intercom#submit"
    end
  end

We are not actually going to use Submit Flow, but Intercom wants us to specify URLs for both Initialize and Submit.

Now use something like localtunnel or [ngrok]https://ngrok.com/ to open internet access to your local app and provide URLs to Intercom app in Developer Hub (see one of screenshots above once again if confused).

Writing the app

Our Initialize Flow will be trivial:

  def init
    render json: {
      canvas: {
        content_url: integrations_intercom_showcase_url
      }
    }
  end

Intercom expects the flow URL to return a JSON object structured in a certain way, with canvas key being at the top of it. Then comes the content part.

If our content is always static, we could provide it right inside the canvas. But the content depends on which language the end user wants, meaning we have to request the content every time user opens the Messenger. This is what content_url is for - it tells Intercom which endpoint in your application to it when Messenger App is being loaded.

IntercomController#showcase method is where the content is being generated. See inline comments for explanation:

  def showcase
    # Grab URL where Intercom Messenger is being loaded
    referrer = URI.parse(params["context"]["referrer"])
    # Parse this URL to determine the language
    I18n.locale = referrer.path.start_with?("/en") ? :en : :ru
    # Render the content for Intercom
    render json: cell(Intercom::Cell::Showcase, nil).render
  end

At mkdev we are using Cells a lot and it felt right to use it for rendering the content for Intercom. Even though content is a JSON object, it's still kind of a view and not some real data. This is how the template of this cell looks like:

{
  "content": {
    "components": [
      {
       "type": "list",
        "items": [
          {
            "type": "item",
            "id": "mentorship-howto",
            "title": "<%= t('.mentorship_title') %>",
            "subtitle": "<%= t('.mentorship_subtitle') %>",
            "image": "<%= image_url("mentorship/kto-takoy-mentor.png") %>",
            "image_width": 64,
            "image_height": 64,
            "roundedImage": false,
            "action": {
              "type": "url",
              "url": "<%= mentorship_url %>"
            }
          },
          {
            "type": "item",
            "id": "pricing",
            "title": "<%= t('.pricing_title') %>",
            "subtitle": "<%= t('.pricing_subtitle') %>",
            "image": "<%= image_url("mentorship/napisat-zapustit-prilozhenie.png") %>",
            "image_width": 64,
            "image_height": 64,
            "roundedImage": false,
            "action": {
              "type": "url",
              "url": "<%= pricing_url %>"
            }
          }
        ]
      }
    ]
  }
}

We use a few Rails helpers to generate the URL, proper image URLs and texts that depend on the locale we set earlier in the controller. All keys in this JSON object are pre-defined by Intercom API.

The result and next steps

Once you are done with testing you need to add the same application to Live version of your Messenger. In case of mkdev, we are now showcasing correct content depending on the language of the user.

Except for figuring out correct JSON structure for the canvas contents, writing our first custom Intercom application was a breeze. It immediately solved one of the problem we had with existing apps and we are looking forward to writing more complex applications for even better experience for our clients and visitors.

There is space for improvement in this basic Multilingual Showcase app: we could use Configure Flow to allow setting the text right inside Intercom interface instead of keeping it in our I18n files. We also didn't look into Submit Flow at all. Please tell us in the comments below if you would be interested in learning how to do it.