Our Intercom API Integration using Ruby as an Example

Pardeep Kullar
Pardeep Kullar

I asked our CTO, Joe d'Elia, to cover how the Intercom API integration with our app is done using Ruby. Below we cover the Rails job that syncs data between our app and Intercom including code and comments.

Related:  See Intercom Related Hacks and Lessons Learned Here

Upscope is a co-browsing (one click to see the customer's screen) service and we use Intercom for live chat support.

While supporting customers we need to have Intercom synced with our app so we so we can quickly help customers.


We need to sync up custom attributes so we can add a link in Intercom directly to the admin page for that customer, see their monthly app usage figures, see when they subscribed and more.

While the Intercom widget integration is a simple copy and paste, it's the database sync between Intercom and our database that requires some work.

We want Intercom to be up to date with the latest data from our database that has changed during their interactions with Upscope.

Below you'll see the Rails job that:

  1. Creates a new user or updates an existing user in Intercom.
  2. Updates which teams they belong to as one user can be part of several teams.
  3. Updates their timezone as sometimes Intercom has better information on that.
  4. Updates their name when Intercom has it but we don't as it's manually inputted.
  5. Updates their phone number, which again might be manually inputted.
  6. Updates Intercom attributes using data from our database.
  7. Merges a lead into a user.
class SyncWithIntercomJob < ApplicationJob
  queue_as :crm_sync
  # Creates a user in intercom if it does not exist. If it does exist, it updates it
  def perform(user)
    return if ENV['INTERCOM_ACCESS_TOKEN'].nil?
    return unless ENV['INTERCOM_SYNC_ENABLED'].true?

    # Gets time zone to reset so we know which it is in case we change it
    old_time_zone = Time.zone

    # Creates the intercom client with that access token
    intercom = Intercom::Client.new(token: ENV['INTERCOM_ACCESS_TOKEN'], handle_rate_limit: true)
    intercom_user = begin
    				  # tries to find user with this id
                      intercom.users.find(user_id: user.id) 
                    # throws exception  
                    rescue Intercom::ResourceNotFound
                      # creates one with that user id
                      intercom.users.create(email: user.email, user_id: user.id)
	# We store intercom id in our db
    user.update metadata: { intercom_user_id: intercom_user.id }

    # Sometimes intercom knows time zone of user and we don't so we use their information on the time zone
    user_time_zone = intercom_user.try(:location_data).try(:timezone)
    if user_time_zone.present?
        # Updates timezone to user
        Time.zone = user_time_zone

        # Saves timezone
        user.update! timezone: user_time_zone
      rescue ArgumentError
        logger.error "Ignored invalid timezone #{user_time_zone}"

    # Adds the teams the user belongs to, remove ones in intercom they no longer belong to as a user can be part of multiple teams on Upscope
    intercom_user_companies_ids = intercom_user.companies.map(&:company_id)
    user_teams_ids = user.teams.map(&:id)
    intercom_user.companies = (intercom_user_companies_ids + user_teams_ids).uniq.map do |id|
        company_id: id,
        remove: !id.in?(user_teams_ids) || nil

    # Sometimes they sign up but we don't have their name but sometimes we add it manually
    if user.name?
      # If we have the name of the person in Upscope, set to intercom
      intercom_user.name = user.name
    elsif intercom_user.name.present? && intercom_user.name.length > 1 && !user.from_oauth?
      # If we have the name in intercom but not Upscope, update Upscope
      user.update name: intercom_user.name
      # Set intercom name to nil
      intercom_user.name = nil

    # Grab phone number from intercom. If phone number is manually added to intercom, we grab it
    if user.phone_number.nil? && intercom_user.phone.present? && !user.from_oauth?
      user.update phone_number: intercom_user.phone

    # Here we are updating Intercom from our records
    intercom_user.email = user.email
    intercom_user.phone = user.phone_number
    intercom_user.created_at = user.created_at

    # Make sure that, in both intercom and our app, the last request is up to date
    user.last_request_at = [user.last_request_at, intercom_user.last_request_at].compact.max
    intercom_user.last_request_at = user.last_request_at

    # Add the custom attributes for that user to intercom, from our database
    intercom_user.custom_attributes = user.as_json(with_everything: true,
                                                   except: [:name, :email, :phone_number, :id, :created_at, :last_request_at, :_type])
                                          .merge(deleted: false)


    # Merge lead if exists. Sometimes there's a lead in intercom which is not a user.If the user was created in the last 24 hours, it looks for a contact with the same email address and merges it.
    if user.created_at > 24.hours.ago
      # Find lead
        intercom_lead = intercom.contacts.find_all(email: user.email).first
        intercom.contacts.convert(intercom_lead, intercom_user) if intercom_lead
        logger.info 'Merged contact into user'
      rescue Intercom::ResourceNotFound

    user.owned_teams.each do |team|
      intercom_company = begin
                           intercom.companies.find(company_id: team.id)
                         rescue Intercom::ResourceNotFound
                           intercom.companies.create(company_id: team.id)
      intercom_company.name = team.domain
      intercom_company.created_at = team.created_at
      intercom_company.monthly_spend = team.monthly_spend
      intercom_company.custom_attributes = team.as_json(
        with_everything: true, except: [:created_at, :id, :_type]
      ).merge(deleted: false)


      team.update metadata: { intercom_company_id: intercom_company.id }
    Time.zone = old_time_zone

Which attributes do we sync up that you might find useful?

Admin link from Intercom to your own website's admin section.

We use this every day, many times a day. When an existing customer asks a question, we need to see their account to understand their context in full. We do this by clicking the Admin link in the Intercom custom attributes list.

Upscope's own instant no-download interactive screen sharing link

When a non-tech customer needs help it's difficult so Upscope built one click no-download interactive screensharing.

You can see what they see instantly AND scroll and click for them on their screen.

Add this using our own Upscope, which is an official app on the intercom app store.

Annual savings attribute so you can remind them to switch

This custom attribute might for example say: Annual saving $316

If they’ve been on a monthly plan for a while, use this during a chat to remind them of savings they'll make by switching to an annual plan.

Other attributes?

You can see the above and other ideas for Intercom attributes listed here.

Add our JS snippet and see the customer's screen from Intercom

Here's a little bit more info on Upscope which not only uses Intercom but also integrates with it.

While chatting, you can click on a link to instantly, without downloads, see what the customer sees and click for them.

We've been tracking feedback and it cuts resolution time by 43% on average though we tend to quote 30% as a good minimum.

To add Upscope to Intercom you add the Upscope javascript snippet on the same page as the Intercom snippet and you have ready to go instant screen sharing.

Start a trial here and if you have questions or need advice, you can ask us on our Intercom live chat. If it's Ruby / API related, Joe's happy to respond.

If you want to see a set of Intercom hacks and lessons learned then see our experience in these articles.

Pardeep Kullar Twitter

Pardeep overlooks growth at Upscope cobrowsing and loves writing about SaaS companies, customer success and customer experience.