Install Messenger in Salesforce Experience Cloud | Community
Skip to main content
Answered

Install Messenger in Salesforce Experience Cloud

  • July 22, 2024
  • 5 replies
  • 206 views

Im trying to install the Messenger app in Salesforce Community. I found this old article but it seems incomplete. 

 

Im wondering if I could get help with this integration. 

 

I Edit the Head Markup in the Salesforce Community with the pre-filled script.

Then in a lightning component I tried to Instantiate the Intercom instance:

import { LightningElement, api } from 'lwc';

export default class IntercomApp extends LightningElement {

connectedCallback() {
try {
window.intercomSettings = {
api_base: 'https://api-iam.intercom.io',
app_id: 'my-app-id-here'
};
} catch (error) {
console.error(error);
}
}
}

I get this warning:

frame-modern.812d2c17.js:1 [Intercom] The App ID in your code snippet has not been set. Set it to your App ID found in settings to complete installation: https://app.intercom.com/a/apps/_/settings/web

 

With this message below:
[Intercom] Launcher is disabled in settings or current page does not match display conditions
 

Best answer by mateusz.leszkiewicz

Hi ​@Mohit Lakde

That’s a good question. I’ve asked our internal AI assistant to help me figure this out and here is the proposition it come up with:
 

On Salesforce (LWC/Experience Cloud) you need to (a) load the Intercom widget correctly under CSP, and (b) boot/update it with your signed user data (user_hash). Here’s a working recipe you can drop in.


1) Allow the domains in CSP Trusted Sites

Setup → Security → CSP Trusted Sites
Add (all as script + connect as applicable):

  • https://widget.intercom.io

  • https://js.intercomcdn.com

  • https://api-iam.intercom.io (or https://api-iam.eu.intercom.io if your Intercom workspace is in EU)


2) Generate user_hash server-side (Apex)

Identity verification for Web requires HMAC-SHA256 over your user_id with your Intercom Identity Verification secret. Do this in Apex and pass it to the LWC (do not compute in the browser).

public with sharing class IntercomCtrl {
@AuraEnabled(cacheable=true)
public static String getUserHash(String userId) {
// Store your Intercom identity secret securely (Named Credential/Protected Custom Metadata)
String secret = System.Label.INTERCOM_IDENTITY_SECRET;
Blob mac = Crypto.generateMac('HmacSHA256', Blob.valueOf(userId), Blob.valueOf(secret));
return EncodingUtil.convertToHex(mac); // Intercom expects hex
}

@AuraEnabled(cacheable=true)
public static User getMe() {
return [SELECT Id, Name, Email, CreatedDate FROM User WHERE Id = :UserInfo.getUserId()];
}
}

3) LWC that boots Intercom with signed user data

This LWC loads the widget, boots once, and sends name, email, user_id, and user_hash.

// intercomMessenger.js
import { LightningElement, track } from 'lwc';
import getUserHash from '@salesforce/apex/IntercomCtrl.getUserHash';
import getMe from '@salesforce/apex/IntercomCtrl.getMe';
import USER_ID from '@salesforce/user/Id';

const APP_ID = 'YOUR_APP_ID'; // <-- replace
const API_BASE = 'https://api-iam.intercom.io'; // or EU endpoint

export default class IntercomMessenger extends LightningElement {
initialized = false;
@track me;
@track hash;

async connectedCallback() {
try {
// fetch user + hash in parallel
const [me, hash] = await Promise.all([
getMe(),
getUserHash({ userId: USER_ID })
]);
this.me = me;
this.hash = hash;

// 1) prepare settings
window.intercomSettings = {
app_id: APP_ID,
api_base: API_BASE,
name: this.me.Name,
email: this.me.Email,
user_id: this.me.Id,
user_hash: this.hash,
created_at: Math.floor(new Date(this.me.CreatedDate).getTime() / 1000) // optional but nice
};

// 2) load the widget script if needed, then boot once
this.loadAndBoot();
} catch (e) {
// If you see "Missing user_hash", double-check step #2 and the secret
// If you see "Intercom not loaded", check CSP Trusted Sites (step #1)
// and that the script actually loads.
// eslint-disable-next-line no-console
console.error(e);
}
}

loadAndBoot() {
if (this.initialized) return;
const doBoot = () => {
if (!window.Intercom) return;
window.Intercom('boot', window.intercomSettings);
this.initialized = true;
};

if (window.Intercom) {
doBoot();
return;
}

const s = document.createElement('script');
s.src = `https://widget.intercom.io/widget/${APP_ID}`;
s.async = true;
s.onload = doBoot;
document.body.appendChild(s);
}

// Optional: shutdown on component unload/logout
disconnectedCallback() {
if (window.Intercom) window.Intercom('shutdown');
}
}

What this fixes vs your snippet

  • Loads the widget script (widget.intercom.io/widget/{APP_ID}); without this, Intercom('boot'| 'update') isn’t available.

  • Sends identity-verified fields (user_id + user_hash) to stop the “Missing user_hash” / “Refusing update for identified user” errors.

  • Uses the correct region via api_base.

  • Boots once (then you can call Intercom('update', {...}) later if profile data changes).


4) SPA/Experience Cloud notes

  • If your site navigates without full page reloads, call window.Intercom('update', {...}) when user attributes change.

  • On logout, call window.Intercom('shutdown').


5) Quick checklist if you still see errors

  • CSP: all three domains allowed (widget, js CDN, api-iam).

  • Region: api_base matches your Intercom data hosting region.

  • user_hash: computed server-side with the same user_id you send to Intercom.

  • Script actually loads: open DevTools → Network, confirm widget/{APP_ID} returns 200.

If you paste this in and replace YOUR_APP_ID (and wire your secret), you’ll get the Messenger booted with name, email, user_id, and a valid user_hash on your authenticated site.

Let me know if that resolved your issue.

5 replies

mateusz.leszkiewicz
Intercom Team
Forum|alt.badge.img+7

Hi Carlos Estrada Toast, It’s Mat from the Support Engineering Team 😀

Did you put your app_id in the snippet? It looks like the error is because the value for app_id is missing in your code.


Hey @mateusz.leszkiewicz, yes I did. I just removed it in the example I provided above for security.


mateusz.leszkiewicz
Intercom Team
Forum|alt.badge.img+7

I’ve created an Intercom conversation to connect you with our Engineers who might help you with that issue.
Please continue this thread there. 


  • New Participant
  • 1 reply
  • October 29, 2025

We are implementing similar intercom messenger on Salesforce authenticated site, but we like to accept username, userId, and user email values, as below. 
So we create lwc with just like above and getting same errors as mentioned in this thread.

import { LightningElement} from 'lwc';

export default class IntercomMessenger extends LightningElement {

connectedCallback() {
try {
window.intercomSettings = {
api_base: 'https://api-iam.intercom.io',
app_id: 'OURKEY'
};
} catch (error) {
console.error(error);
}
}
}

@mateusz.leszkiewicz  How did we resolved this error? 


mateusz.leszkiewicz
Intercom Team
Forum|alt.badge.img+7

Hi ​@Mohit Lakde

That’s a good question. I’ve asked our internal AI assistant to help me figure this out and here is the proposition it come up with:
 

On Salesforce (LWC/Experience Cloud) you need to (a) load the Intercom widget correctly under CSP, and (b) boot/update it with your signed user data (user_hash). Here’s a working recipe you can drop in.


1) Allow the domains in CSP Trusted Sites

Setup → Security → CSP Trusted Sites
Add (all as script + connect as applicable):

  • https://widget.intercom.io

  • https://js.intercomcdn.com

  • https://api-iam.intercom.io (or https://api-iam.eu.intercom.io if your Intercom workspace is in EU)


2) Generate user_hash server-side (Apex)

Identity verification for Web requires HMAC-SHA256 over your user_id with your Intercom Identity Verification secret. Do this in Apex and pass it to the LWC (do not compute in the browser).

public with sharing class IntercomCtrl {
@AuraEnabled(cacheable=true)
public static String getUserHash(String userId) {
// Store your Intercom identity secret securely (Named Credential/Protected Custom Metadata)
String secret = System.Label.INTERCOM_IDENTITY_SECRET;
Blob mac = Crypto.generateMac('HmacSHA256', Blob.valueOf(userId), Blob.valueOf(secret));
return EncodingUtil.convertToHex(mac); // Intercom expects hex
}

@AuraEnabled(cacheable=true)
public static User getMe() {
return [SELECT Id, Name, Email, CreatedDate FROM User WHERE Id = :UserInfo.getUserId()];
}
}

3) LWC that boots Intercom with signed user data

This LWC loads the widget, boots once, and sends name, email, user_id, and user_hash.

// intercomMessenger.js
import { LightningElement, track } from 'lwc';
import getUserHash from '@salesforce/apex/IntercomCtrl.getUserHash';
import getMe from '@salesforce/apex/IntercomCtrl.getMe';
import USER_ID from '@salesforce/user/Id';

const APP_ID = 'YOUR_APP_ID'; // <-- replace
const API_BASE = 'https://api-iam.intercom.io'; // or EU endpoint

export default class IntercomMessenger extends LightningElement {
initialized = false;
@track me;
@track hash;

async connectedCallback() {
try {
// fetch user + hash in parallel
const [me, hash] = await Promise.all([
getMe(),
getUserHash({ userId: USER_ID })
]);
this.me = me;
this.hash = hash;

// 1) prepare settings
window.intercomSettings = {
app_id: APP_ID,
api_base: API_BASE,
name: this.me.Name,
email: this.me.Email,
user_id: this.me.Id,
user_hash: this.hash,
created_at: Math.floor(new Date(this.me.CreatedDate).getTime() / 1000) // optional but nice
};

// 2) load the widget script if needed, then boot once
this.loadAndBoot();
} catch (e) {
// If you see "Missing user_hash", double-check step #2 and the secret
// If you see "Intercom not loaded", check CSP Trusted Sites (step #1)
// and that the script actually loads.
// eslint-disable-next-line no-console
console.error(e);
}
}

loadAndBoot() {
if (this.initialized) return;
const doBoot = () => {
if (!window.Intercom) return;
window.Intercom('boot', window.intercomSettings);
this.initialized = true;
};

if (window.Intercom) {
doBoot();
return;
}

const s = document.createElement('script');
s.src = `https://widget.intercom.io/widget/${APP_ID}`;
s.async = true;
s.onload = doBoot;
document.body.appendChild(s);
}

// Optional: shutdown on component unload/logout
disconnectedCallback() {
if (window.Intercom) window.Intercom('shutdown');
}
}

What this fixes vs your snippet

  • Loads the widget script (widget.intercom.io/widget/{APP_ID}); without this, Intercom('boot'| 'update') isn’t available.

  • Sends identity-verified fields (user_id + user_hash) to stop the “Missing user_hash” / “Refusing update for identified user” errors.

  • Uses the correct region via api_base.

  • Boots once (then you can call Intercom('update', {...}) later if profile data changes).


4) SPA/Experience Cloud notes

  • If your site navigates without full page reloads, call window.Intercom('update', {...}) when user attributes change.

  • On logout, call window.Intercom('shutdown').


5) Quick checklist if you still see errors

  • CSP: all three domains allowed (widget, js CDN, api-iam).

  • Region: api_base matches your Intercom data hosting region.

  • user_hash: computed server-side with the same user_id you send to Intercom.

  • Script actually loads: open DevTools → Network, confirm widget/{APP_ID} returns 200.

If you paste this in and replace YOUR_APP_ID (and wire your secret), you’ll get the Messenger booted with name, email, user_id, and a valid user_hash on your authenticated site.

Let me know if that resolved your issue.