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 endend
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.