How to Integrate Intercom with Your Headless CMS
Connect Intercom to your headless CMS so Help Center articles, Messenger prompts, and support guidance stay current from the same structured content your docs team publishes.
What is Intercom?
Intercom is a customer service platform for live chat, support tickets, Help Center articles, in-app messages, and customer communication. Support, success, marketing, and product teams use it to answer questions in the Messenger, route conversations, publish help content, and communicate with customers inside an app or website. It sits in a crowded support and communication category, but it’s one of the better-known tools for combining chat, automation, and self-serve support in one workspace.
Why integrate Intercom with a headless CMS?
If your support articles live in one place, your product docs live in another, and your Intercom Help Center has to be updated by hand, drift is guaranteed. A pricing FAQ gets updated on your website on Tuesday, but the Intercom article still shows the old plan limits on Friday. Now your support team is answering the same question in conversations, and customers are seeing conflicting information.
Connecting Intercom to a headless CMS solves that by treating support content as structured data instead of copy-pasted pages. In Sanity, the AI Content Operating System, you can model a support article with fields like product area, audience, plan, locale, related features, and Intercom article ID. GROQ selects exactly what Intercom needs, webhooks fire when an editor publishes, and Functions can call Intercom’s REST API without you running a separate sync server.
The trade-off is that you need to define your schema and field mapping up front. That takes more thought than pasting HTML into Intercom. But once the mapping exists, updates are repeatable, testable, and much less dependent on someone remembering to update three places after every product launch.
Architecture overview
A typical Intercom integration starts when an editor publishes a support article in Sanity Studio. The published document lands in the Content Lake as structured JSON, including fields such as title, slug, excerpt, Portable Text body, locale, Intercom collection ID, and an existing Intercom article ID if the article was synced before. A Sanity webhook or Sanity Function listens for publish events on documents like supportArticle. The trigger can use a GROQ filter, for example _type == "supportArticle" && defined(slug.current), so unrelated content changes don’t call Intercom. The sync logic then runs a GROQ query to fetch only the fields Intercom needs, including joined references such as category name or product area. The server-side handler converts Portable Text to HTML, then calls Intercom’s Articles API. If the Sanity document already has an intercomArticleId, the handler sends a PUT request to https://api.intercom.io/articles/{id}. If not, it sends a POST request to https://api.intercom.io/articles with title, description, body, author_id, state, parent_id, and parent_type. The returned Intercom article ID can be written back to Sanity so later publishes update the same article instead of creating duplicates. From there, customers see the updated article in Intercom Messenger, Help Center search, and any support workflows that reference that article. Your website, mobile app, Intercom Help Center, and AI agents can all read from the same structured source instead of each maintaining a separate copy.
Common use cases
Sync Help Center articles
Publish support articles in Sanity Studio, then create or update matching Intercom Articles when the content goes live.
Keep Messenger answers current
Use the same product guidance that powers your docs site to feed Intercom Messenger article suggestions and self-serve support flows.
Coordinate launch communications
Publish release notes once, then reuse the approved copy for your website, Intercom Help Center, and in-app announcement content.
Route customers to the right content
Tag Sanity articles by plan, product area, and audience so Intercom content can match the customer’s question and context.
Step-by-step integration
- 1
Set up Intercom access
Create or use an Intercom workspace, enable your Help Center, and install the Messenger on your site or app. In the Intercom Developer Hub, create an app or access token with permissions for Articles. Record the access token, an Intercom author ID, and the collection or section ID where synced articles should appear.
- 2
Model support content in Sanity Studio
Create a supportArticle schema with fields like title, slug, excerpt, body, productArea, audience, locale, intercomCollectionId, intercomArticleId, and publishState. Keep Intercom-specific IDs as fields so editors can see sync status without editing API details.
- 3
Create the publish trigger
Use a Sanity webhook or Function triggered on content mutations. Filter it to published support documents, for example _type == "supportArticle" && !(_id in path("drafts.**")). This avoids calling Intercom for drafts, unrelated documents, or internal metadata updates.
- 4
Fetch the exact payload with GROQ
Inside the handler, use @sanity/client and a GROQ query to fetch the fields Intercom needs. Join references such as product area or category if they should be included in the article description, tags, or body.
- 5
Call Intercom’s Articles API
Convert Portable Text to HTML, then POST to /articles for new documents or PUT to /articles/{id} for updates. Use the Intercom-Version header, handle non-200 responses, and store the returned article ID back in Sanity to prevent duplicate articles.
- 6
Test the end-user experience
Publish a test article, confirm it appears in Intercom Help Center, and open the Messenger as a test user. Check formatting, links, collection placement, and locale behavior before enabling the sync for production content.
Code example
import {createClient} from "@sanity/client";
import {toHTML} from "@portabletext/to-html";
import {NextRequest, NextResponse} from "next/server";
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: "2025-02-19",
token: process.env.SANITY_WRITE_TOKEN!,
useCdn: false
});
const articleQuery = `*[_id == $id][0]{
_id,title,excerpt,body,intercomArticleId,
"collectionId": intercomCollectionId,
"slug": slug.current
}`;
async function callIntercom(path: string, method: "POST" | "PUT", body: unknown) {
const res = await fetch(`https://api.intercom.io${path}`, {
method,
headers: {
Authorization: `Bearer ${process.env.INTERCOM_ACCESS_TOKEN}`,
Accept: "application/json",
"Content-Type": "application/json",
"Intercom-Version": "2.11"
},
body: JSON.stringify(body)
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
export async function POST(req: NextRequest) {
const payload = await req.json();
const id = payload.ids?.updated?.[0] || payload.ids?.created?.[0];
if (!id || id.startsWith("drafts.")) return NextResponse.json({skipped: true});
const article = await sanity.fetch(articleQuery, {id});
if (!article) return NextResponse.json({skipped: true});
const intercomBody = {
title: article.title,
description: article.excerpt,
body: toHTML(article.body || []),
author_id: process.env.INTERCOM_AUTHOR_ID,
state: "published",
parent_id: article.collectionId || process.env.INTERCOM_COLLECTION_ID,
parent_type: "collection"
};
const saved = article.intercomArticleId
? await callIntercom(`/articles/${article.intercomArticleId}`, "PUT", intercomBody)
: await callIntercom("/articles", "POST", intercomBody);
if (!article.intercomArticleId && saved.id) {
await sanity.patch(article._id).set({intercomArticleId: String(saved.id)}).commit();
}
return NextResponse.json({ok: true, intercomArticleId: saved.id});
}How Sanity + Intercom works
Build your Intercom integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Intercom with the rest of your customer support experience.
Start building free →CMS approaches to Intercom
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Support article structure | Articles often start as rendered pages, so syncing to Intercom may require copying HTML or cleaning markup before it can be used. | The Content Lake stores typed JSON, so Intercom can receive predictable fields like title, excerpt, body, locale, and collection ID. |
| Publish-to-Intercom timing | Teams often update Intercom manually after publishing, which creates gaps when policies or launch details change. | Webhooks or Functions can call Intercom on publish events without polling or running separate infrastructure. |
| Field-level control | Integrations may receive whole pages, including navigation, layout markup, or unrelated fields. | GROQ can fetch exactly the Intercom payload in one query, including referenced product areas, categories, and locale metadata. |
| Editorial workflow | Support teams may rely on screenshots, tickets, or copy-paste reviews before updating Intercom. | Sanity Studio can include fields for Intercom sync status, preview links, Tasks, comments, and content releases. |
| AI support context | AI agents often crawl public pages, which can miss private support content or use stale rendered output. | Agent Context gives production AI agents scoped, read-only access to structured support content that can also feed Intercom. |
| Setup trade-off | Manual updates are simple at first, but they don’t scale well once you have dozens of articles, locales, and product areas. | You need to plan the schema and Intercom field mapping, but Functions, webhooks, and GROQ keep the integration code small. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Slack
Notify support, docs, and product teams in Slack when Intercom-facing articles are published or need review.
Sanity + Microsoft Teams
Send Teams alerts for support content releases, policy updates, and articles that affect customer conversations.
Sanity + Zendesk
Reuse the same structured support content across Zendesk help articles, macros, and agent workflows.