
Our Intercom API Integration using Ruby as an Example
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.
Why?
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:
- Creates a new user or updates an existing user in Intercom.
- Updates which teams they belong to as one user can be part of several teams.
- Updates their timezone as sometimes Intercom has better information on that.
- Updates their name when Intercom has it but we don't as it's manually inputted.
- Updates their phone number, which again might be manually inputted.
- Updates Intercom attributes using data from our database.
- 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)
end
# 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?
begin
# 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}"
end
end
# 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
}
end
# 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
else
# Set intercom name to nil
intercom_user.name = nil
end
# 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
user.reload
end
# 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)
intercom.users.save(intercom_user)
# 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
begin
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
nil
end
end
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)
end
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)
intercom.companies.save(intercom_company)
team.update metadata: { intercom_company_id: intercom_company.id }
end
ensure
Time.zone = old_time_zone
end
end
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.