To integrate Intercom with Lovable, generate an access token from your Intercom Developer Hub app, store it in Cloud → Secrets, then build Edge Functions that proxy calls to the Intercom REST API for conversations, contacts, companies, and articles. Intercom's conversational-first approach makes it ideal for building in-app messaging features, user onboarding flows, and help center widgets inside Lovable apps.
Add conversational messaging and help center features to Lovable with Intercom's API
Intercom pioneered the in-app messaging bubble that has become standard in modern SaaS products. Unlike Zendesk's ticket-queue approach, Intercom is designed for conversations — real-time messaging between users and support teams, triggered onboarding flows, and proactive outreach based on user behavior. For Lovable apps targeting paying SaaS customers, Intercom integration creates a native-feeling support experience directly embedded in your product.
Intercom's integration approach has two distinct layers. The first is the Messenger widget — a JavaScript-based chat bubble embedded in your app that handles real-time conversation UI natively. The second is the REST API — used for creating and updating contacts (Intercom's term for users), managing companies, fetching conversation history, and accessing the help center knowledge base. For Lovable apps, the Messenger widget handles real-time chat while Edge Functions handle the API operations that require server-side credentials.
The Intercom REST API v2 uses Bearer token authentication from a custom Developer Hub application. Unlike HubSpot's Private Apps, Intercom access tokens do not expire — they are permanent until you regenerate them. API scopes determine what the token can access: reading contacts, writing messages, managing companies, and so on. All API calls include the header 'Intercom-Version: 2.11' to target the current stable API version.
The most common Lovable + Intercom pattern is a hybrid: embed the Messenger widget for real-time chat, use Edge Functions to create or update Intercom contact records when users sign up or perform key actions, and query the Intercom Articles API to render help center content inside your app without redirecting users to a separate help site.
Integration method
Intercom has no native Lovable connector. All messaging and CRM operations are proxied through Lovable Edge Functions that call the Intercom REST API v2. The access token from a custom Intercom app is stored in Cloud → Secrets and accessed via Deno.env.get(), keeping credentials server-side and avoiding CORS restrictions on direct browser calls to Intercom's API.
Prerequisites
- A Lovable project with Lovable Cloud enabled (Edge Functions require a deployed app)
- An Intercom workspace (free trial available at intercom.com — note that API access requires a paid plan for production use)
- Access to create apps in Intercom Developer Hub (developer.intercom.com)
- Basic familiarity with Intercom concepts: contacts (users), companies, conversations, and the Messenger widget
Step-by-step guide
Create an Intercom app in Developer Hub and get an access token
Create an Intercom app in Developer Hub and get an access token
Intercom uses app-based authentication via its Developer Hub. Each integration requires creating a custom app in Developer Hub, which provides an access token scoped to the permissions you configure. Go to developer.intercom.com and sign in with your Intercom account. Click 'New App' in the top right. Give your app a name (e.g., 'Lovable Integration') and select the workspace it should connect to. If you have multiple workspaces (e.g., development and production), create separate apps for each. After creation, go to the 'Authentication' section of your new app. You will see an 'Access Token' — copy this immediately. Intercom access tokens are long strings starting with 'dG9r...' (base64 encoded). Unlike many OAuth2 services, Intercom access tokens do not expire automatically, so you do not need to implement token refresh logic. Configure the appropriate API scopes for your integration. Click 'Permissions' in the left sidebar of your app settings. Enable: - Read contacts (for fetching user data) - Write contacts (for creating/updating contacts) - Read conversations (for fetching support conversations) - Write conversations (for sending messages) - Read articles (for help center content) Enable only the scopes you actually need. Overly permissive tokens create unnecessary security exposure. Also note your Intercom App ID — a short alphanumeric string visible in your workspace URL or in the app settings. This is used for configuring the Messenger widget (not the API), but you will need it when adding the chat widget to your Lovable app. Store it as INTERCOM_APP_ID in secrets alongside the access token.
Pro tip: Intercom Developer Hub apps exist per workspace — if you want to test with a sandbox workspace and deploy with your production workspace, you need two separate apps with two separate access tokens. Store them as INTERCOM_ACCESS_TOKEN_DEV and INTERCOM_ACCESS_TOKEN_PROD and switch based on your deployment environment.
Expected result: An Intercom Developer Hub app is created with the appropriate API scopes. You have copied the access token and noted the App ID. Both are ready to be stored in Lovable Cloud → Secrets.
Store credentials in Cloud Secrets and configure the Messenger widget
Store credentials in Cloud Secrets and configure the Messenger widget
Add Intercom credentials to Lovable's Cloud Secrets panel. In Lovable, click the '+' icon at the top to open the Cloud panel, navigate to Secrets, and add: - INTERCOM_ACCESS_TOKEN — the access token from Developer Hub (the long dG9r... string) - INTERCOM_APP_ID — your workspace App ID (short alphanumeric, used for the Messenger widget) The App ID is different from the access token. It is the public identifier used to initialize the Messenger widget in your frontend code. It is safe to include in client-side code (it is not a secret) but storing it in secrets makes it easy to switch workspaces without code changes. For the Messenger widget (the live chat bubble), Intercom provides a JavaScript snippet that initializes in the browser. In Lovable, you can initialize the Messenger by adding the Intercom script to your React app's main component. The Intercom object is added to window and initialized with your App ID. You can also pass user identity information (user_id, email, name) when initializing, which associates the conversation with the correct Intercom contact. For user identity verification (a security feature that prevents users from impersonating each other in the Messenger), Intercom requires you to generate an HMAC signature server-side using your Identity Verification Secret. This signature is computed in an Edge Function and passed to the frontend at runtime — it should never be computed client-side.
Add the Intercom Messenger widget to my Lovable app. Initialize window.Intercom with the INTERCOM_APP_ID from environment config (not secrets — it is a public value). When a user is logged in, pass their userId, email, and name to the Intercom initialization to associate conversations with their contact record. Show the Messenger on all pages by calling window.Intercom('show') when the help button is clicked.
Paste this in Lovable chat
1// src/components/IntercomProvider.tsx2import { useEffect } from 'react'3import { useAuth } from '@/hooks/useAuth'45declare global {6 interface Window {7 Intercom: (...args: unknown[]) => void8 intercomSettings: Record<string, unknown>9 }10}1112export function IntercomProvider({ children }: { children: React.ReactNode }) {13 const { user } = useAuth()14 const appId = import.meta.env.VITE_INTERCOM_APP_ID1516 useEffect(() => {17 if (!appId) return1819 // Load Intercom script20 const script = document.createElement('script')21 script.innerHTML = `22 (function(){var w=window;var ic=w.Intercom;23 if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}24 else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};25 w.Intercom=i;var l=function(){var s=d.createElement('script');26 s.type='text/javascript';s.async=true;27 s.src='https://widget.intercom.io/widget/${appId}';28 var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};29 if(document.readyState==='complete'){l();}else if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();30 `31 document.head.appendChild(script)3233 return () => { document.head.removeChild(script) }34 }, [appId])3536 useEffect(() => {37 if (!window.Intercom || !appId) return38 if (user) {39 window.Intercom('boot', {40 app_id: appId,41 user_id: user.id,42 email: user.email,43 name: user.user_metadata?.full_name || user.email,44 })45 } else {46 window.Intercom('boot', { app_id: appId })47 }48 }, [user, appId])4950 return <>{children}</>51}Pro tip: Add VITE_INTERCOM_APP_ID to your Lovable project's environment config (not Cloud Secrets — it is a public value safe for the frontend). The access token (INTERCOM_ACCESS_TOKEN) remains in Cloud Secrets and is only used by Edge Functions.
Expected result: The Intercom Messenger bubble appears in the bottom-right corner of your deployed Lovable app. When clicked, it opens a conversation window. Logged-in users are identified by their email in the Intercom inbox.
Create the Intercom REST API proxy Edge Function
Create the Intercom REST API proxy Edge Function
Build the Edge Function that handles Intercom REST API operations: creating and updating contacts, fetching conversations, and querying help center articles. These server-side operations require the access token and cannot be performed from the browser. Intercom API v2 uses consistent REST patterns. All requests include the 'Intercom-Version: 2.11' header to target a stable API version. Contacts are created via POST /contacts with a 'role' field ('user' for customers, 'lead' for anonymous visitors). The upsert pattern for contacts uses POST /contacts with an external_id that matches your app's user ID — if a contact with that external_id exists, it is updated; otherwise, a new contact is created. Companies are created separately via POST /companies and linked to contacts via POST /contacts/{id}/companies. For SaaS products with account-based pricing, maintaining company records in Intercom enables segment-based messaging and company-level analytics. For article search, the Articles API is at GET /articles with a ?phrase= search parameter. Articles can also be browsed by collection via GET /help_center/collections/{id}/articles. Intercom uses cursor-based pagination with a 'pages' object in list responses containing 'next' and 'total_pages'. For small result sets (under 50 items), one page is usually sufficient. For larger datasets, implement cursor pagination by passing the 'starting_after' query parameter.
Create an Edge Function called intercom-api at supabase/functions/intercom-api/index.ts. Accept POST with { action, params }. Support actions: upsert_contact, get_contact, search_articles, send_message, list_conversations. Use INTERCOM_ACCESS_TOKEN from secrets. Include 'Intercom-Version: 2.11' header on all requests. Return the Intercom API response. Include CORS headers.
Paste this in Lovable chat
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'23const corsHeaders = {4 'Access-Control-Allow-Origin': '*',5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',6}78const INTERCOM_BASE = 'https://api.intercom.io'910function intercomHeaders() {11 const token = Deno.env.get('INTERCOM_ACCESS_TOKEN')12 if (!token) throw new Error('INTERCOM_ACCESS_TOKEN not configured')13 return {14 Authorization: `Bearer ${token}`,15 'Content-Type': 'application/json',16 'Intercom-Version': '2.11',17 }18}1920serve(async (req) => {21 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })2223 try {24 const { action, params = {} } = await req.json()25 const headers = intercomHeaders()26 let resp: Response2728 switch (action) {29 case 'upsert_contact':30 resp = await fetch(`${INTERCOM_BASE}/contacts`, {31 method: 'POST',32 headers,33 body: JSON.stringify({34 role: params.role || 'user',35 external_id: params.userId,36 email: params.email,37 name: params.name,38 custom_attributes: params.customAttributes || {},39 }),40 })41 break4243 case 'get_contact':44 resp = await fetch(`${INTERCOM_BASE}/contacts/${params.contactId}`, { headers })45 break4647 case 'search_articles':48 resp = await fetch(`${INTERCOM_BASE}/articles?phrase=${encodeURIComponent(params.query || '')}&state=published`, { headers })49 break5051 case 'send_message':52 resp = await fetch(`${INTERCOM_BASE}/messages`, {53 method: 'POST',54 headers,55 body: JSON.stringify({56 message_type: params.messageType || 'inapp',57 subject: params.subject || '',58 body: params.body,59 template: 'plain',60 from: { type: 'admin', id: params.adminId },61 to: { type: 'user', id: params.contactId },62 }),63 })64 break6566 case 'list_conversations':67 resp = await fetch(`${INTERCOM_BASE}/conversations?contact_id=${params.contactId}&display_as=plaintext`, { headers })68 break6970 default:71 return new Response(JSON.stringify({ error: 'Unknown action' }), {72 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },73 })74 }7576 const data = await resp.json()77 return new Response(JSON.stringify(data), {78 status: resp.ok ? 200 : resp.status,79 headers: { ...corsHeaders, 'Content-Type': 'application/json' },80 })81 } catch (error) {82 return new Response(JSON.stringify({ error: error.message }), {83 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },84 })85 }86})Pro tip: When upserting contacts, use your app's internal user ID as the external_id. This ensures Intercom correctly identifies returning users and merges contact data rather than creating duplicates. The external_id is the bridge between your Supabase user records and Intercom contacts.
Expected result: The intercom-api Edge Function deploys in Cloud → Edge Functions. Test with action 'search_articles' and a query term — published articles from your Intercom help center should appear in the response.
Common use cases
Sync new user signups to Intercom as contacts with custom attributes
When a new user registers in your Lovable app, create or update their Intercom contact record with custom attributes like plan, signup source, company size, and feature flags. This gives your Intercom inbox rich context about each user, enabling personalized support messages and automated onboarding sequences triggered by contact attributes. Contacts created via API are immediately visible to your support team in the Intercom inbox.
Create an Edge Function called intercom-contact that accepts { userId, email, name, company, plan, signupSource } and creates or updates an Intercom contact using INTERCOM_ACCESS_TOKEN from secrets. Set custom attributes for plan and signupSource. Use the upsert pattern (POST to contacts with role: 'user'). Return the contact ID. Call this from my registration success handler alongside the Supabase user creation.
Copy this prompt to try it in Lovable
Render Intercom help center articles inside your Lovable app
Instead of redirecting users to a separate help.yourcompany.com site, fetch Intercom Articles directly into your Lovable app. An Edge Function queries the Intercom Articles API for articles matching a search term or category, and a React component renders the content inline. Users get contextual help without leaving your product, and you maintain one source of truth in Intercom for all help content.
Create an Edge Function called intercom-articles that accepts { query, collectionId } and searches Intercom help center articles using INTERCOM_ACCESS_TOKEN from secrets. Return article id, title, body (HTML), and url. Then build a Help Center component in Lovable with a search bar and article list. When a user clicks an article, render the body content in a modal with the article title as the heading.
Copy this prompt to try it in Lovable
Proactive in-app message when users hit a usage limit
When a user hits a usage limit in your Lovable app (API quota, storage full, feature gated), automatically send them a proactive Intercom message via the API offering to upgrade or explaining next steps. The Edge Function creates an in-app message directed at the user's contact ID with a call-to-action linking to your pricing page or upgrade flow.
Create an Edge Function called intercom-message that accepts { contactId, messageType, body, ctaText, ctaUrl } and sends an in-app message to an Intercom contact using INTERCOM_ACCESS_TOKEN from secrets. Use the admin-initiated conversation endpoint. Return the conversation ID. Call this function when a user's storage hits 90% capacity, sending a message about upgrading their plan.
Copy this prompt to try it in Lovable
Troubleshooting
Intercom API returns 401 Unauthorized on all requests
Cause: The access token is missing, expired, or was created for a different Intercom workspace than the one you are targeting.
Solution: In Cloud → Secrets, verify INTERCOM_ACCESS_TOKEN is present and correct. In Intercom Developer Hub, check the app's 'Authentication' section to confirm the token matches. Also verify the app is connected to the correct workspace — if you have multiple Intercom workspaces, each requires its own Developer Hub app and token.
Intercom Messenger widget does not appear on the page after adding the script
Cause: The Intercom app is not correctly initialized, or the VITE_INTERCOM_APP_ID environment variable is not available in the browser bundle.
Solution: Ensure VITE_INTERCOM_APP_ID is set as a build-time environment variable (not in Cloud Secrets, which are server-only). In Lovable, this should be configured as a regular environment variable accessible via import.meta.env.VITE_INTERCOM_APP_ID. Also verify the boot call includes the app_id field — Intercom will silently fail to show the widget if app_id is missing or undefined.
Intercom returns 'User not found' when trying to send a message or fetch conversations
Cause: The contact ID used in the request does not exist in the Intercom workspace, or the contact was created in a different workspace.
Solution: Contact IDs in Intercom are workspace-specific. Always upsert the contact using your app's user ID as external_id before performing operations, and use the Intercom contact ID from the upsert response for subsequent calls. Store the Intercom contact ID in your Supabase users table to avoid repeated lookups.
Duplicate contacts are being created in Intercom for the same user
Cause: Contacts are being created without a consistent external_id, so Intercom cannot match new contacts to existing ones.
Solution: Always include the external_id field in contact creation, set to your app's internal user ID. Intercom uses external_id as the deduplication key when upserting. Without it, each API call creates a new contact even for the same email address. If duplicates already exist, use the Intercom dashboard's 'Merge contacts' feature to consolidate them.
1// Always include external_id in contact creation:2body: JSON.stringify({3 role: 'user',4 external_id: params.userId, // required for deduplication5 email: params.email,6 name: params.name,7})Best practices
- Always set external_id to your app's user ID when creating Intercom contacts — this is the key to deduplication and ensures Intercom can match users across multiple touch points.
- Use the Intercom Messenger widget for real-time conversations and the REST API via Edge Functions for programmatic operations — never try to do real-time messaging through API polling.
- Store the Intercom contact ID in your Supabase users table after the first upsert so future operations use direct contact IDs instead of search queries.
- Implement Intercom Identity Verification (HMAC signature) for production apps — it prevents users from impersonating each other in the Messenger by requiring a server-generated signature.
- Use custom_attributes sparingly and name them clearly — Intercom limits the number of custom attributes and poorly named attributes clutter your contact records for your support team.
- Trigger contact upserts on significant user events (signup, upgrade, feature activation) rather than on every page view to avoid unnecessary API calls and rate limiting.
- Always include the 'Intercom-Version: 2.11' header in API requests — without it, Intercom uses an older default API version that may have different response shapes.
Alternatives
Choose Zendesk if your support model is ticket-queue based with agent SLAs and a help center — Zendesk's traditional ticketing system is better suited to high-volume support operations.
Choose Freshdesk if you want a more affordable alternative to both Intercom and Zendesk with a generous free plan and similar multi-channel support capabilities.
Choose LiveChat if you need only real-time chat without Intercom's broader product suite — LiveChat is simpler, cheaper, and easier to embed in React applications.
Frequently asked questions
Can I use Intercom for free with Lovable?
Intercom offers a 14-day free trial and a free plan for early-stage startups (less than $1M in funding with fewer than 5 team seats — apply at intercom.com/early-stage). The standard paid plans start at $74/month for the Essential plan. API access is available on all plans. For most Lovable integrations, the Messenger widget and basic contact management work within the free trial period while you evaluate fit.
What is the difference between Intercom contacts, leads, and users?
In Intercom's current data model (API v2), all individuals are 'contacts' with a 'role' property: 'user' for known, logged-in customers and 'lead' for anonymous visitors. Older Intercom documentation uses the terms 'users' and 'leads' as separate endpoints — these were merged in API v2. When creating contacts from your Lovable app for known users, always use role: 'user' with their email and external_id.
How do I prevent duplicate Intercom contacts from multiple sign-up paths?
The key is always passing an external_id set to your app's internal user ID when creating contacts. Intercom treats external_id as the deduplication key — if a contact with the same external_id exists, it is updated rather than duplicated. Never create contacts without external_id set, and use the upsert endpoint (POST /contacts) rather than checking for existence first.
Can I embed Intercom help center articles inside my Lovable app without iframes?
Yes — the Intercom Articles API returns article content as HTML body text, which you can render directly in your React components. Fetch articles via your Edge Function's search_articles action and render the HTML content using React's dangerouslySetInnerHTML, or use a sanitization library like DOMPurify to clean the HTML before rendering. This gives you fully embedded help content styled to match your app's design, with no iframe required.
How do I handle Intercom Identity Verification for security?
Intercom Identity Verification prevents users from changing their user_id or email in the browser to impersonate other users. Enable it in your Intercom app settings (Settings → Installation → Identity Verification). Then create an Edge Function that accepts your app's user ID and returns an HMAC-SHA256 signature computed using your Identity Verification Secret (stored in Cloud → Secrets). Pass this signature to the Intercom Messenger initialization as 'user_hash'. For complex security setups, RapidDev's team can help implement the full verification flow.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation