How to Integrate Discord with Your Headless CMS
Send publish-ready content from your headless CMS to Discord channels the moment it goes live, with structured fields mapped to messages, embeds, and bot workflows.
What is Discord?
Discord is a communication platform organized around servers, channels, direct messages, voice, video, bots, slash commands, and webhooks. Itβs widely used by gaming communities, open-source projects, developer relations teams, customer communities, and internal teams that need fast group communication. Discord apps connect through the Discord API, Gateway API, interactions, incoming webhooks, and SDKs like discord.js.
Why integrate Discord with a headless CMS?
If your launch notes, changelog entries, event pages, or community updates live in one system while your audience lives in Discord, someone usually ends up copy-pasting. That creates small but real problems: the wrong version gets posted, links are missing, channel owners forget updates, and moderators spend time answering questions that were already covered in published content.
Architecture overview
A typical Discord integration starts when an editor publishes content in Sanity Studio. That write updates the structured document in the Content Lake. A GROQ-powered webhook filters for the exact event you care about, for example published documents where _type == "releaseNote" and notifyDiscord == true. The webhook can call your own endpoint, or a Sanity Function can run the server-side sync logic without a separate worker. The handler receives the mutation payload, uses @sanity/client and GROQ to fetch only the fields Discord needs, maps those fields into a Discord message or embed, and calls Discordβs REST API with a bot token. Discord then posts the message to the configured channel, where end users see it like any other server update. For simple one-way posts, an incoming Discord webhook can work. For channel routing, embeds, bot identity, retries, slash commands, or future edits, a Discord bot using discord.js is usually the better fit.
Common use cases
Publish announcements to Discord
Post product launches, blog posts, or changelog entries to #announcements with the title, summary, author, and canonical URL pulled from Sanity.
Notify beta communities
Route beta release notes to a private Discord channel based on a Sanity field like audience: "beta-testers".
Keep support teams current
Send docs updates to #support so moderators know when troubleshooting steps, pricing rules, or API limits change.
Power Discord bot answers
Let a Discord bot answer slash commands by querying structured content, such as FAQs, event details, or product docs.
Step-by-step integration
- 1
Create a Discord app and bot
In the Discord Developer Portal, create an application, add a bot, copy the bot token, and invite it to your server with the bot scope. For posting embeds, grant View Channels, Send Messages, and Embed Links. Install the SDK with npm install discord.js @sanity/client.
- 2
Model Discord-ready content in Sanity Studio
Add fields that map cleanly to Discord, such as title, slug, excerpt, author, audience, discordChannelId, notifyDiscord, and publishedAt. Keep long body content in Portable Text, but send a short excerpt to Discord because plain messages have a 2,000 character limit.
- 3
Create a publish trigger
Use a GROQ-powered webhook or a Sanity Function. A useful webhook filter is _type == "post" && !(_id in path("drafts.**")) && notifyDiscord == true. Send a small projection, such as {"_id": _id}, then fetch the full message payload server-side.
- 4
Map Sanity fields to Discord messages
In your handler, fetch the published document with GROQ, build a Discord embed, and call Discordβs REST API with the bot token. Watch Discord limits: 10 embeds per message, 6,000 total embed characters, and rate limits returned as HTTP 429.
- 5
Test in a private channel first
Publish a test document, confirm the webhook fires once, check that links point to production URLs, and log Discord response IDs. If duplicate posts matter, write the Discord message ID back to Sanity or keep an idempotency record in your own system.
- 6
Build the frontend experience from the same content
Use the same structured document for your website, mobile app, and Discord update. For example, the Discord post can link to a Next.js route that renders the full article from the Content Lake with GROQ.
Code example
import {createClient} from '@sanity/client'
import {REST, Routes} from 'discord.js'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_READ_TOKEN!,
useCdn: false
})
const discord = new REST({version: '10'}).setToken(
process.env.DISCORD_BOT_TOKEN!
)
export async function POST(req: Request) {
const {_id} = await req.json()
const post = await sanity.fetch(
`*[_id == $id][0]{
title,
excerpt,
"url": "https://example.com/posts/" + slug.current,
"author": author->name
}`,
{id: _id}
)
if (!post) return Response.json({ok: false}, {status: 404})
await discord.post(
Routes.channelMessages(process.env.DISCORD_CHANNEL_ID!),
{
body: {
content: `New post: **${post.title}**`,
embeds: [{
title: post.title,
description: post.excerpt,
url: post.url,
fields: [{name: 'Author', value: post.author || 'Editorial'}]
}]
}
}
)
return Response.json({ok: true})
}How Sanity + Discord works
Build your Discord integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Discord to your AI content operations.
Start building free βCMS approaches to Discord
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Discord announcement payloads | Content is often tied to pages or HTML output, so Discord messages need cleanup before posting. | Content Lake documents are typed JSON, and GROQ can return a Discord-ready payload with title, excerpt, URL, and referenced author data. |
| Publish-time notifications | Teams often rely on plugins, cron jobs, or manual copy-paste into Discord channels. | GROQ-powered webhooks or Functions can trigger only on the content types and fields that should notify Discord. |
| Channel routing | Routing usually lives outside the content model, so editors need a separate checklist for where to post. | Sanity Studio can model fields like audience, discordChannelId, and notifyDiscord, with custom validation before publishing. |
| Bot-powered content lookup | Discord bots often search public pages, which can return outdated or poorly structured answers. | Bots can query structured content with GROQ, or production AI agents can use Agent Context for scoped, read-only access. |
| Sync logic and operations | Custom servers are usually needed for retries, formatting, and Discord API calls. | Functions can run server-side sync logic on content events. For very high-volume Discord bots, you may still want a dedicated queue. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Slack
Send editorial alerts, release updates, and review requests from Sanity to the Slack channels your team already watches.
Sanity + Microsoft Teams
Notify internal teams when pages, docs, or product updates are published from your structured content workflow.
Sanity + Twilio
Turn structured content events into SMS or WhatsApp notifications for time-sensitive updates outside Discord.