Skip to main content
RapidDev - Software Development Agency
replit-integrationsStandard API Integration

How to Integrate Replit with Intercom

To integrate Replit with Intercom, generate an Intercom Access Token from your Developer Hub app, store it in Replit Secrets (lock icon in sidebar), then call the Intercom REST API to manage contacts, conversations, and messages. For real-time events, register a webhook endpoint on your deployed Replit app URL to receive customer activity notifications.

What you'll learn

  • How to create an Intercom app and generate an Access Token
  • How to store Intercom credentials securely in Replit Secrets
  • How to create, update, and search Intercom contacts from backend code
  • How to send messages and manage conversations via the Intercom API
  • How to receive real-time Intercom webhook events on a deployed Replit endpoint
Book a free consultation
4.9Clutch rating ⭐
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read20 minutesCommunicationMarch 2026RapidDev Engineering Team
TL;DR

To integrate Replit with Intercom, generate an Intercom Access Token from your Developer Hub app, store it in Replit Secrets (lock icon in sidebar), then call the Intercom REST API to manage contacts, conversations, and messages. For real-time events, register a webhook endpoint on your deployed Replit app URL to receive customer activity notifications.

Why Connect Replit to Intercom?

Intercom sits at the intersection of customer support, sales chat, and product communication. Connecting your Replit application to Intercom unlocks the ability to programmatically create and update contacts when users sign up in your app, trigger automated messages based on user behavior in your product, sync conversation data to other systems, and build custom integrations that Intercom's native workflow tools cannot handle alone.

The most common integration pattern is a contact sync pipeline: when a user registers in your Replit app, you call the Intercom API to create or update a contact record with their name, email, and custom attributes (like plan type, company size, or feature usage). This ensures your support team always has up-to-date context when a customer starts a conversation. You can also tag contacts automatically, assign conversations to specific team inboxes based on business rules, and send targeted messages to specific user segments.

Webhooks make the integration bidirectional β€” when a customer sends a message or closes a conversation in Intercom, your Replit app receives an event payload and can trigger actions like updating a CRM record, sending a Slack notification to your team, or logging the interaction to a database. This tutorial covers both directions: pushing data into Intercom and receiving events from Intercom in real time.

Integration method

Standard API Integration

The Replit-Intercom integration works by authenticating with an Intercom Access Token stored in Replit Secrets, then calling the Intercom REST API from your backend server to create or update contacts, send messages, retrieve conversations, and manage inbox assignments. For event-driven workflows, you register a webhook in the Intercom Developer Hub pointing to your deployed Replit app URL, which then receives real-time POSTs whenever customers start conversations, reply, or trigger other actions.

Prerequisites

  • An Intercom account (a free trial or paid workspace works)
  • A Replit account with a Node.js or Python Repl created
  • Basic familiarity with REST APIs and HTTP requests
  • A deployed Replit app (for webhook endpoints β€” use Autoscale deployment)

Step-by-step guide

1

Create an Intercom Developer App and Get Your Access Token

Intercom uses a developer app model for API authentication. Every API call requires an Access Token associated with a specific Intercom workspace. To generate one, navigate to your Intercom account and click your profile avatar in the bottom-left, then select 'Settings'. Scroll down to the 'Integrations' section and click 'Developer Hub', or go directly to app.intercom.com/a/developer-hub. In the Developer Hub, click 'New App' in the top right. Give it a descriptive name like 'Replit Integration' and select the workspace you want to connect. Click 'Create App'. On the app detail page, navigate to the 'Authentication' tab on the left sidebar. You will see your Access Token β€” this is a permanent token tied to this app and workspace. Click the copy icon next to the Access Token. This token gives read/write access to your entire Intercom workspace based on the permissions you configure. For the permissions, stay on the Authentication tab and review the 'Access Permissions' section β€” enable the objects your integration needs: Contacts (read/write), Conversations (read/write), and Messages (write). Click 'Save Changes'. Note: if you are building an integration for customers' Intercom accounts (not just your own workspace), you would use OAuth instead. For personal integrations connecting your own Replit app to your own Intercom account, the Access Token approach is simpler and works well.

Pro tip: Intercom's Developer Hub also shows your app's webhook configuration, test event delivery, and API usage logs. Bookmark this page β€” you will return to it when configuring webhooks in a later step.

Expected result: A new Intercom developer app is created and you have copied the Access Token to use in the next step.

2

Store Your Intercom Access Token in Replit Secrets

Click the lock icon (πŸ”’) in the Replit sidebar to open the Secrets panel. Click 'New Secret', enter INTERCOM_ACCESS_TOKEN as the key, and paste your copied Access Token as the value. Click 'Add Secret'. The token is now encrypted with AES-256 and stored separately from your code β€” it will never appear in your file tree, Git history, or Replit's public visibility. Replit's Secret Scanner actively monitors for API key patterns being pasted directly into code files and will prompt you to move them to Secrets. Intercom tokens match common API key patterns, so if you accidentally paste the token into a code file, you will see a warning. Always route credentials through Secrets first. If you need to test different Intercom workspaces (e.g., development vs production), create a separate secret key like INTERCOM_ACCESS_TOKEN_PROD and INTERCOM_ACCESS_TOKEN_DEV, then reference the appropriate one based on an environment variable like NODE_ENV. Access the token in your code: - Node.js: const token = process.env.INTERCOM_ACCESS_TOKEN - Python: token = os.environ['INTERCOM_ACCESS_TOKEN'] After saving, the secret is immediately available to your running Repl without a restart. For deployed apps, new secrets require a redeploy to take effect.

Pro tip: After adding the secret, verify it loaded correctly by adding a quick test: console.log('Token loaded:', !!process.env.INTERCOM_ACCESS_TOKEN) β€” this prints true/false without revealing the actual value.

Expected result: INTERCOM_ACCESS_TOKEN appears in the Secrets panel with the value hidden as dots.

3

Create and Update Intercom Contacts

Contacts are the core data model in Intercom β€” every user or lead that interacts with your product becomes a contact. The Intercom API lets you create new contacts, search for existing ones by email, update attributes, and tag contacts for segmentation. The code below implements a complete contact management module with both Node.js and Python examples. The Node.js version uses the built-in fetch API (Node 18+, which is Replit's default). The Python version uses the requests library. Install packages if needed: - Node.js: no additional packages required (uses built-in fetch) - Python: pip install requests flask

intercom-contacts.js
1// intercom-contacts.js β€” Node.js contact management
2const INTERCOM_BASE = 'https://api.intercom.io';
3
4async function intercomRequest(method, endpoint, body = null) {
5 const token = process.env.INTERCOM_ACCESS_TOKEN;
6 if (!token) throw new Error('INTERCOM_ACCESS_TOKEN secret not set');
7
8 const options = {
9 method,
10 headers: {
11 'Authorization': `Bearer ${token}`,
12 'Accept': 'application/json',
13 'Content-Type': 'application/json',
14 'Intercom-Version': '2.11'
15 }
16 };
17 if (body) options.body = JSON.stringify(body);
18
19 const response = await fetch(`${INTERCOM_BASE}${endpoint}`, options);
20 const data = await response.json();
21
22 if (!response.ok) {
23 throw new Error(`Intercom API error ${response.status}: ${JSON.stringify(data)}`);
24 }
25 return data;
26}
27
28// Create or update a contact by email (upsert)
29async function upsertContact({ email, name, phone, customAttributes = {} }) {
30 // Search for existing contact first
31 const searchResult = await intercomRequest('POST', '/contacts/search', {
32 query: {
33 field: 'email',
34 operator: '=',
35 value: email
36 }
37 });
38
39 if (searchResult.total_count > 0) {
40 // Update existing contact
41 const contactId = searchResult.data[0].id;
42 const updated = await intercomRequest('PUT', `/contacts/${contactId}`, {
43 name,
44 phone,
45 custom_attributes: customAttributes
46 });
47 console.log(`Updated contact: ${contactId}`);
48 return updated;
49 } else {
50 // Create new contact
51 const created = await intercomRequest('POST', '/contacts', {
52 role: 'user',
53 email,
54 name,
55 phone,
56 custom_attributes: customAttributes
57 });
58 console.log(`Created contact: ${created.id}`);
59 return created;
60 }
61}
62
63// Tag a contact
64async function tagContact(contactId, tagName) {
65 // First, get or create the tag
66 const tags = await intercomRequest('GET', '/tags');
67 let tag = tags.data.find(t => t.name === tagName);
68
69 if (!tag) {
70 tag = await intercomRequest('POST', '/tags', { name: tagName });
71 }
72
73 // Apply tag to contact
74 await intercomRequest('POST', `/contacts/${contactId}/tags`, { id: tag.id });
75 return tag;
76}
77
78module.exports = { upsertContact, tagContact, intercomRequest };

Pro tip: The Intercom-Version header is important β€” always pin to a specific version (currently 2.11) to avoid breaking changes when Intercom releases API updates. Check Intercom's changelog at developers.intercom.com/changelog for deprecation notices.

Expected result: The upsertContact function creates a new contact in Intercom if the email does not exist, or updates the existing contact if it does.

4

Build an API Server for Contact and Conversation Management

With the contact module ready, build an Express server that exposes HTTP endpoints your frontend or other services can call. This server handles the most common Intercom operations: syncing a user on registration, retrieving recent conversations, and sending a proactive message to a contact. The conversations endpoint uses Intercom's search API to retrieve recent open conversations, which is more efficient than pagination for dashboard use cases. The messaging endpoint uses the admin-initiated message endpoint to send a message from a specific admin (your Intercom teammate) to a user.

server.js
1// server.js β€” Intercom Express API server
2const express = require('express');
3const { upsertContact, tagContact, intercomRequest } = require('./intercom-contacts');
4
5const app = express();
6app.use(express.json());
7
8// POST /sync-user β€” call this when a user registers or updates profile
9app.post('/sync-user', async (req, res) => {
10 try {
11 const { email, name, phone, plan, company } = req.body;
12 if (!email) return res.status(400).json({ error: 'email required' });
13
14 const contact = await upsertContact({
15 email,
16 name,
17 phone,
18 customAttributes: {
19 plan: plan || 'free',
20 company: company || ''
21 }
22 });
23
24 // Auto-tag based on plan
25 if (plan === 'enterprise') {
26 await tagContact(contact.id, 'Enterprise');
27 }
28
29 res.json({ success: true, contactId: contact.id });
30 } catch (err) {
31 console.error(err.message);
32 res.status(500).json({ error: err.message });
33 }
34});
35
36// GET /conversations β€” recent open conversations
37app.get('/conversations', async (req, res) => {
38 try {
39 const result = await intercomRequest('GET',
40 '/conversations?display_as=plaintext&per_page=20');
41 const conversations = result.conversations.map(c => ({
42 id: c.id,
43 subject: c.source?.subject || 'No subject',
44 state: c.state,
45 assignee: c.assignee?.name || 'Unassigned',
46 created_at: new Date(c.created_at * 1000).toISOString()
47 }));
48 res.json({ conversations });
49 } catch (err) {
50 res.status(500).json({ error: err.message });
51 }
52});
53
54// POST /send-message β€” send proactive message to a user
55app.post('/send-message', async (req, res) => {
56 try {
57 const { email, subject, body, adminId } = req.body;
58 if (!email || !body) {
59 return res.status(400).json({ error: 'email and body required' });
60 }
61
62 // Find the contact
63 const searchResult = await intercomRequest('POST', '/contacts/search', {
64 query: { field: 'email', operator: '=', value: email }
65 });
66
67 if (searchResult.total_count === 0) {
68 return res.status(404).json({ error: 'Contact not found in Intercom' });
69 }
70
71 const contactId = searchResult.data[0].id;
72
73 // Send message as email from admin
74 const message = await intercomRequest('POST', '/messages', {
75 message_type: 'email',
76 subject: subject || 'Message from our team',
77 body,
78 template: 'plain',
79 from: { type: 'admin', id: adminId || process.env.INTERCOM_ADMIN_ID },
80 to: { type: 'user', id: contactId }
81 });
82
83 res.json({ success: true, messageId: message.id });
84 } catch (err) {
85 res.status(500).json({ error: err.message });
86 }
87});
88
89app.listen(3000, '0.0.0.0', () => {
90 console.log('Intercom integration server running on port 3000');
91});

Pro tip: To find your Intercom Admin ID (required for sending messages), call GET /admins with your Access Token and look for your name in the result list. Store it as an INTERCOM_ADMIN_ID secret so you do not hardcode it.

Expected result: POST /sync-user creates or updates a contact in Intercom. GET /conversations returns a list of recent open conversations.

5

Set Up Webhooks for Real-Time Events

Webhooks allow Intercom to push events to your Replit app in real time β€” when a customer sends a message, closes a conversation, or when a new lead is created. This is more efficient than polling the API continuously. First, deploy your Replit app to get a stable URL. Click the Deploy button in the top-right corner of the Replit editor, choose 'Autoscale', and complete the deployment. Copy your deployment URL (e.g., https://your-app.replit.app). Next, configure the webhook in the Intercom Developer Hub: go to app.intercom.com/a/developer-hub, select your app, click 'Webhooks' in the left sidebar, and click 'Add a webhook endpoint'. Enter your deployment URL + /intercom-webhook (e.g., https://your-app.replit.app/intercom-webhook). Select the topics you want to receive β€” common choices include conversation.user.created, conversation.user.replied, contact.created, and contact.signed_up. Click 'Save'. The webhook handler below verifies the signature using the Client Secret from your Developer Hub app (find it under Authentication > Client Secret), adds it as a Replit secret named INTERCOM_CLIENT_SECRET.

webhook_handler.py
1# webhook_handler.py β€” Python Flask Intercom webhook receiver
2import os
3import hmac
4import hashlib
5import json
6from flask import Flask, request, jsonify
7
8app = Flask(__name__)
9
10def verify_intercom_signature(payload_body, signature_header):
11 """Verify the webhook is genuinely from Intercom."""
12 client_secret = os.environ.get('INTERCOM_CLIENT_SECRET', '')
13 if not client_secret or not signature_header:
14 return False
15
16 # Intercom sends sha1=<hex_digest>
17 expected = 'sha1=' + hmac.new(
18 client_secret.encode('utf-8'),
19 payload_body,
20 hashlib.sha1
21 ).hexdigest()
22
23 return hmac.compare_digest(expected, signature_header)
24
25@app.route('/intercom-webhook', methods=['POST'])
26def intercom_webhook():
27 # Verify signature
28 signature = request.headers.get('X-Hub-Signature', '')
29 if not verify_intercom_signature(request.data, signature):
30 return jsonify({'error': 'Invalid signature'}), 401
31
32 payload = request.json
33 topic = payload.get('topic', 'unknown')
34 data = payload.get('data', {}).get('item', {})
35
36 print(f'Intercom webhook: topic={topic}')
37
38 if topic == 'conversation.user.created':
39 user_email = data.get('source', {}).get('author', {}).get('email')
40 subject = data.get('source', {}).get('subject', 'No subject')
41 print(f'New conversation from {user_email}: {subject}')
42 # Add your logic: notify Slack, update CRM, etc.
43
44 elif topic == 'conversation.user.replied':
45 conv_id = data.get('id')
46 print(f'User replied on conversation {conv_id}')
47
48 elif topic == 'contact.created':
49 contact_email = data.get('email')
50 print(f'New Intercom contact: {contact_email}')
51
52 # Always return 200 quickly β€” process async if needed
53 return jsonify({'status': 'received'}), 200
54
55if __name__ == '__main__':
56 app.run(host='0.0.0.0', port=3000)

Pro tip: Intercom expects your webhook endpoint to respond within 30 seconds. If your processing logic is slow (database writes, external API calls), return 200 immediately and process the event asynchronously using a background task queue.

Expected result: Intercom delivers test webhook events to your deployment URL and they appear in your Replit console logs.

Common use cases

User Registration Contact Sync

Automatically create or update an Intercom contact record every time a user signs up or updates their profile in your Replit app. Include custom attributes like subscription plan, company name, and account creation date so your support team always has context in the conversation inbox.

Replit Prompt

Build a Node.js Express server with a /user-registered POST endpoint. When called with user data (email, name, plan, company), it should upsert the contact in Intercom using the Contacts API, setting custom attributes for plan and company. Store the INTERCOM_ACCESS_TOKEN in environment variables.

Copy this prompt to try it in Replit

Conversation Assignment Bot

Build a webhook handler that receives incoming Intercom conversation events and automatically assigns them to the correct team inbox based on keywords, user attributes, or tags β€” for example, routing enterprise customers to a dedicated support team and routing billing questions to the finance team.

Replit Prompt

Create a Flask server that receives Intercom conversation.user.created webhook events, reads the conversation subject and user plan attribute, then assigns the conversation to the correct team using the Intercom Conversations API. Return 200 OK for all valid webhook deliveries.

Copy this prompt to try it in Replit

Support Metrics Dashboard

Build a real-time dashboard that queries the Intercom API to display key support metrics: open conversation count, average response time, conversations by team inbox, and contact volume over time β€” giving your team operational visibility without leaving your Replit app.

Replit Prompt

Create an Express API server with endpoints that query Intercom conversations filtered by status and assignee, calculate average response times from timestamps, and return aggregated metrics as JSON for a frontend dashboard to display as charts.

Copy this prompt to try it in Replit

Troubleshooting

API returns 401 Unauthorized on every request

Cause: The most common causes are: the Access Token was not saved correctly in Replit Secrets, the token was copied with extra whitespace or newline characters, or the Intercom app's permissions do not include the resource being accessed.

Solution: Open Replit Secrets, delete the INTERCOM_ACCESS_TOKEN secret, and re-add it carefully (avoid copy-pasting with surrounding spaces). Verify the token works by running a simple test request: curl -H 'Authorization: Bearer YOUR_TOKEN' https://api.intercom.io/me from the Replit Shell. Also check the Developer Hub app's Authentication tab to confirm the required permissions are enabled.

typescript
1// Quick token validation in Node.js
2const response = await fetch('https://api.intercom.io/me', {
3 headers: { 'Authorization': `Bearer ${process.env.INTERCOM_ACCESS_TOKEN}` }
4});
5console.log(response.status, await response.json());

Webhook events are not being received on the Replit endpoint

Cause: The webhook is likely configured with the development URL (the temporary *.replit.dev URL that only works while the editor is open) instead of the deployment URL (*.replit.app). Intercom webhooks require a publicly accessible, always-on URL.

Solution: Deploy your Replit app using Autoscale deployment to get a stable https://your-app.replit.app URL. Update the webhook endpoint URL in the Intercom Developer Hub under Webhooks > your webhook > Edit. Use the Intercom webhook tester (send test event button) to confirm delivery after updating.

Contact search returns 0 results even though the contact exists in Intercom

Cause: The Contacts search API uses exact matching by default. If the email case does not match exactly (e.g., User@Example.com vs user@example.com), or if the contact was created as a 'lead' type while you are searching for 'user' type, the search may return empty.

Solution: Intercom contact emails are case-insensitive in their system but the search query should use lowercase. Convert all email addresses to lowercase before searching. Also note that contacts can be of type 'user' or 'lead' β€” a search query does not filter by type by default, but check that you are creating contacts with the correct role field.

typescript
1// Always lowercase emails before searching
2const email = userEmail.toLowerCase().trim();
3const result = await intercomRequest('POST', '/contacts/search', {
4 query: { field: 'email', operator: '=', value: email }
5});

Sending a message returns 'admin_not_found' or 422 error

Cause: The adminId used in the from field of the message request does not match a valid admin in your Intercom workspace, or the admin does not have permission to send outbound messages.

Solution: Find valid admin IDs by making a GET request to https://api.intercom.io/admins with your Access Token. Store the correct admin ID as the INTERCOM_ADMIN_ID secret. Ensure the admin has the 'Send messages' permission in their Intercom teammate role settings.

typescript
1// List all admins to find correct IDs
2const admins = await intercomRequest('GET', '/admins');
3console.log(admins.admins.map(a => ({ id: a.id, name: a.name, email: a.email })));

Best practices

  • Always upsert contacts using a search-before-create pattern rather than assuming a contact does not exist β€” duplicate contacts in Intercom can cause confusion for your support team and skew reporting metrics.
  • Pin the Intercom-Version header to a specific API version (currently 2.11) in all requests to prevent unexpected breaking changes when Intercom releases updates.
  • Store your Intercom Access Token, Client Secret, and Admin ID in Replit Secrets (lock icon) rather than hardcoding them β€” Replit's Secret Scanner will warn you if it detects API key patterns in code files.
  • Return 200 from webhook endpoints immediately and process events asynchronously β€” Intercom times out webhook deliveries after 30 seconds and will retry, potentially causing duplicate processing if your handler is slow.
  • Use Intercom's webhook delivery log in the Developer Hub (Webhooks > your endpoint > Recent deliveries) to debug failed webhook deliveries and inspect the exact payloads being sent.
  • Deploy on Autoscale for webhook endpoints rather than leaving your app in development mode β€” the development URL only works while your Replit editor is open, causing missed webhook events.
  • Implement rate limit handling in your API client β€” Intercom enforces rate limits of 1,000 requests per minute per access token. Add retry logic with exponential backoff for 429 responses.

Alternatives

Frequently asked questions

How do I store my Intercom API token in Replit?

Click the lock icon (πŸ”’) in the Replit sidebar to open the Secrets panel, then add a new secret with the key INTERCOM_ACCESS_TOKEN and your token as the value. Access it in Node.js as process.env.INTERCOM_ACCESS_TOKEN or in Python as os.environ['INTERCOM_ACCESS_TOKEN']. Never paste the token directly into your code files.

Does Replit work with Intercom webhooks?

Yes, but you must use a deployed Replit app (Autoscale or Reserved VM) rather than the development preview URL. The development URL is temporary and only works while your Replit browser tab is open. Deploy your app to get a stable https://your-app.replit.app URL, then register that URL in the Intercom Developer Hub under Webhooks.

What is the difference between Intercom Access Token and Client Secret?

The Access Token is used to authenticate all API calls (contacts, conversations, messages). The Client Secret is used to verify webhook signatures β€” you use it to compute an HMAC hash and compare it against the X-Hub-Signature header Intercom sends with each webhook request. Both are found in your Developer Hub app under the Authentication tab.

Can I use Replit with Intercom for free?

Intercom's API is accessible on all plans, including the Starter plan (~$74/month). There is no free tier for production Intercom usage beyond the 14-day trial. The Intercom Developer Hub and test workspace are free, so you can build and test your Replit integration without a paid plan during development.

How do I avoid creating duplicate contacts in Intercom?

Use the Contacts search API to check if a contact exists by email before creating a new one. This search-before-create pattern (upsert) ensures you update existing contacts rather than creating duplicates. Intercom also provides a dedicated upsert endpoint that handles this automatically β€” POST to /contacts with the email and it will merge with an existing contact or create a new one.

How do I find my Intercom Admin ID for sending messages?

Make a GET request to https://api.intercom.io/admins with your Authorization header. The response lists all teammates in your workspace with their IDs, names, and emails. Store your admin ID as an INTERCOM_ADMIN_ID secret in Replit and reference it when sending outbound messages.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation β€” no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.