How to Integrate Zendesk with Your Headless CMS
Connect Zendesk to your headless CMS so approved help articles, macros, and support content can publish to Zendesk Guide and agent workflows without copy-paste.
What is Zendesk?
Zendesk is a customer service platform used for ticketing, help centers, messaging, voice support, automation, and customer data. Support, success, operations, and product teams use it to answer customer questions across channels and keep a searchable knowledge base in Zendesk Guide. It has a large market presence in customer support software, with APIs for tickets, users, organizations, macros, help center articles, and custom apps.
Why integrate Zendesk with a headless CMS?
Support teams lose time when product content and support content live in separate systems. A release note gets published on the website, but the Zendesk Guide article is still outdated. A pricing page changes, but agents keep using an old macro. A support bot links to an article that says the wrong plan limit. These are small misses until they show up in 400 tickets after a launch.
Connecting Zendesk to a headless CMS helps you treat support content as part of the same content operation as your website, docs, mobile app, and product surfaces. With Sanity's AI Content Operating System, support articles can be modeled as structured fields in the Content Lake, selected with GROQ, and synced to Zendesk the moment they publish. You don't need to scrape HTML from a page or ask support ops to paste the same paragraph into Zendesk Guide, macros, and ticket sidebars.
The trade-off is that you need to design the content model and sync rules up front. For example, you'll decide which Sanity document types map to Zendesk Guide articles, which fields become labels, which locales publish to which Help Center locales, and whether drafts should create Zendesk drafts or stay out of Zendesk entirely. That setup takes work, but it replaces recurring manual checks with a predictable publish path.
Architecture overview
A typical Sanity and Zendesk integration starts with structured support content in Sanity's Content Lake. In Sanity Studio, editors publish a supportArticle document with fields like title, slug, body, product, locale, Zendesk section ID, labels, and an optional Zendesk article ID. On publish, a Sanity webhook fires. You can send that webhook to a Sanity Function, a Next.js route, or another small server-side endpoint. The handler reads the document ID from the webhook payload, runs a GROQ query to fetch only the fields Zendesk needs, converts Portable Text to HTML, and calls Zendesk's Help Center REST API. New articles use POST /api/v2/help_center/{locale}/sections/{section_id}/articles.json. Existing articles use PUT /api/v2/help_center/articles/{article_id}.json. Authentication usually uses a Zendesk email address plus API token with the Basic auth format email/token:api_token. After Zendesk receives the article, it becomes available in Zendesk Guide for customers, in agent search for support teams, and in any Zendesk bot or workflow that reads Guide content. If you save the returned Zendesk article ID back to Sanity, future publishes can update the same Zendesk article instead of creating duplicates. Functions are useful here because they run server-side sync logic on content events without a separate queue, cron job, or worker service.
Common use cases
Publish approved help articles to Zendesk Guide
Sync supportArticle documents from Sanity to Zendesk Guide sections so customers see the same approved content your website and app use.
Keep agent macros aligned with product changes
Generate or update Zendesk macros from structured troubleshooting steps, plan limits, refund policies, or onboarding snippets.
Route tickets with content metadata
Use product, region, plan, or issue type fields from Sanity to set Zendesk labels, tags, or custom ticket fields that help triage.
Sync localized support content
Map Sanity locale fields to Zendesk Help Center locales so translated articles publish to the right language versions.
Step-by-step integration
- 1
Set up Zendesk API access
In Zendesk Admin Center, enable API token access, create or choose a service user, and save the subdomain, email address, and API token. If you're building an agent sidebar app, install Zendesk's ZCLI with npm install -g @zendesk/zcli. For article sync, the Zendesk Help Center REST API is plain HTTP, so a runtime SDK isn't required.
- 2
Model support content in Sanity Studio
Create a supportArticle schema with title, slug, body, locale, product reference, labels, zendesk.sectionId, and zendesk.articleId. Keep Zendesk IDs in fields instead of hardcoding them in the sync function, so support ops can route articles to the right Guide section.
- 3
Create the publish trigger
Create a Sanity webhook or Sanity Function that runs on publish events for supportArticle documents. A practical webhook filter is _type == "supportArticle" && !(_id in path("drafts.**")), so draft edits don't update Zendesk.
- 4
Fetch the exact payload with GROQ
Inside the handler, use @sanity/client and GROQ to fetch only the fields Zendesk needs, including joins such as product->name or category->zendeskSectionId. This keeps the Zendesk payload small and avoids field mapping logic scattered across the codebase.
- 5
Call Zendesk's API
Convert Portable Text to HTML, then call Zendesk's Help Center Articles API. Use POST to create articles when zendesk.articleId is empty and PUT to update articles when it exists. Save the returned article ID back to Sanity.
- 6
Test the end-to-end experience
Publish one article to a Zendesk sandbox first. Confirm the section, locale, labels, permissions, formatting, and search behavior in Zendesk Guide. Then test an update, an unpublish path, and a failed API call before turning it on for production content.
Code example
A minimal webhook handler that receives a Sanity publish event, fetches the article with GROQ, and creates or updates a Zendesk Guide article.
import {createClient} from "@sanity/client";
import {toHTML} from "@portabletext/to-html";
export const runtime = "nodejs";
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 zdBase = `https://${process.env.ZENDESK_SUBDOMAIN}.zendesk.com`;
const zdAuth = Buffer.from(
`${process.env.ZENDESK_EMAIL}/token:${process.env.ZENDESK_API_TOKEN}`
).toString("base64");
export async function POST(req: Request) {
const event = await req.json();
const id = event._id || event.ids?.updated?.[0] || event.ids?.created?.[0];
const article = await sanity.fetch(`
*[_id == $id][0]{
_id,
title,
body,
locale,
labels,
"sectionId": zendesk.sectionId,
"articleId": zendesk.articleId
}
`, {id});
if (!article?.sectionId) {
return Response.json({skipped: true, reason: "Missing Zendesk section"});
}
const payload = {
article: {
title: article.title,
body: toHTML(article.body || []),
locale: article.locale || "en-us",
label_names: article.labels || [],
draft: false
}
};
const url = article.articleId
? `${zdBase}/api/v2/help_center/articles/${article.articleId}.json`
: `${zdBase}/api/v2/help_center/${payload.article.locale}/sections/${article.sectionId}/articles.json`;
const res = await fetch(url, {
method: article.articleId ? "PUT" : "POST",
headers: {
Authorization: `Basic ${zdAuth}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (!res.ok) throw new Error(await res.text());
const data = await res.json();
if (!article.articleId) {
await sanity.patch(article._id)
.set({"zendesk.articleId": data.article.id})
.commit();
}
return Response.json({ok: true, zendeskArticleId: data.article.id});
}How Sanity + Zendesk works
Build your Zendesk integration on Sanity
Sanity's AI Content Operating System gives you the structured content foundation, real-time event system, and flexible APIs to connect Zendesk with your support workflows.
Start building free →CMS approaches to Zendesk
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Zendesk Guide article sync | Often starts from rendered pages or editor-created HTML, which makes article mapping brittle. | Uses structured documents in the Content Lake, then runs webhook or Function logic to create and update Zendesk articles. |
| Field-level control for Zendesk payloads | Templates and page fields can mix presentation with support content, so API payloads need cleanup. | Uses GROQ to select and join exactly the title, body, locale, labels, and section ID Zendesk needs. |
| Real-time updates for agents | Support teams may copy changes into Zendesk manually after publishing elsewhere. | Webhooks and Functions can trigger server-side Zendesk updates on publish without a separate worker service. |
| Localized support content | Locale handling is often tied to pages, making it harder to map cleanly to Zendesk Help Center locales. | Models locale, fallback, and Zendesk section fields in schemas, then queries the right version for each Help Center locale. |
| Agent and bot context | Agents often rely on links, pasted snippets, or manually maintained macros. | Can feed Zendesk and also expose scoped, schema-aware content to production AI agents through Agent Context. |
| Implementation trade-off | Fast if your team only needs manual publishing into Zendesk, but harder to keep consistent at scale. | Requires schema design and sync code, but gives developers versioned schemas and editors a tailored Sanity Studio workflow. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Slack
Send publish alerts, review requests, and support content change notifications from Sanity into Slack channels.
Sanity + Microsoft Teams
Notify support, product, and legal teams in Microsoft Teams when Zendesk-bound content is ready to review or publish.
Sanity + Intercom
Use structured product and help content from Sanity to keep Intercom articles, messages, and support flows aligned.