How to Integrate Coveo with Your Headless CMS
Connect Coveo to structured content so your site, support portal, or commerce experience can return fresh, relevant results the moment content changes.
What is Coveo?
Coveo is an enterprise search and discovery platform used for website search, ecommerce search, customer support, and workplace knowledge discovery. It indexes content from many sources, applies relevance tuning through query pipelines and machine learning models, and serves results through APIs, Coveo Atomic components, or the Coveo Headless library.
Why integrate Coveo with a headless CMS?
Search gets messy when your content lives in one place and your search index lives somewhere else. A product page gets updated, a help article gets archived, or a legal disclaimer changes, but Coveo keeps serving the old version until someone runs a batch job or manually reindexes a source. That lag can mean wrong prices, outdated support answers, or search results that point to unpublished pages.
Architecture overview
A typical Sanity and Coveo integration starts when an editor publishes or updates content in Sanity Studio. That mutation writes structured JSON to the Content Lake. A Sanity webhook, filtered to published document types, or a Sanity Function, triggered by the content event, receives the document ID. The handler uses @sanity/client and a GROQ query to fetch only the fields Coveo should index, such as title, slug, excerpt, Portable Text body, category references, locale, and publish date. The handler then calls the Coveo Push API with a stable documentId, usually the canonical URL, and sends a JSON payload containing searchable text in data plus structured metadata fields. Coveo indexes that document inside a Push source, applies source mappings and query pipeline rules, and returns results to the user through the Search API, Coveo Atomic, or @coveo/headless. For deletions or unpublishing, the same event flow should call the Coveo Push API DELETE endpoint with the same documentId so stale results leave the index.
Common use cases
Site search with fresh editorial content
Index articles, landing pages, FAQs, and docs in Coveo as soon as editors publish them in Sanity Studio.
Commerce search with product storytelling
Send product copy, buying guides, categories, and campaign content from Sanity to Coveo so shoppers can find products by intent, use case, or feature.
Support portal deflection
Push approved help articles and troubleshooting content into Coveo so customers find the right answer before they open a ticket.
Localized search experiences
Index locale-specific titles, slugs, body text, and metadata so Coveo can return the right language and region-specific result.
Step-by-step integration
- 1
Set up Coveo
Create or choose a Coveo organization, add a Push source, and create an API key with permission to add, update, and delete documents in that source. Note the organization ID, source ID, and API key. If you're building the frontend with Coveo components, install @coveo/headless or use Coveo Atomic.
- 2
Model searchable content in Sanity Studio
Define schemas for the content you want Coveo to index. For an article, useful fields include title, slug, excerpt, body, category references, locale, audience, publish date, and a boolean like excludeFromSearch. Keep ranking and filtering needs in mind before you ship the schema.
- 3
Create a GROQ projection for indexing
Write one GROQ query that returns the exact fields Coveo needs. Resolve references, flatten Portable Text into searchable text, and include metadata fields that map to Coveo fields, such as contenttype, category, locale, and publishdate.
- 4
Create the sync mechanism
Use a Sanity Function for server-side processing on content events, or create a webhook listener in your own API route. Filter events to published document types so drafts don't enter the search index. Add delete and unpublish handling from the start.
- 5
Push documents to Coveo
Call the Coveo Push API PUT endpoint for each published document. Use the canonical URL as documentId, send the main searchable text in data, and send structured fields in metadata. For removed content, call the DELETE endpoint with the same documentId.
- 6
Test relevance and the frontend
Search in the Coveo administration console first, then build the user interface with Coveo Atomic, @coveo/headless, or the Search API. Test publish, update, delete, locale filtering, and protected content before you point production traffic at it.
Code example
import {createClient} from '@sanity/client';
import type {VercelRequest, VercelResponse} from '@vercel/node';
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-02-19',
token: process.env.SANITY_READ_TOKEN,
useCdn: false,
});
export default async function handler(req: VercelRequest, res: VercelResponse) {
const id = String(req.body._id || '').replace(/^drafts\./, '');
const doc = await sanity.fetch(`*[_id == $id][0]{
_id, _type, title, slug, excerpt, publishedAt,
"category": category->title,
body[]{children[]{text}}
}`, {id});
if (!doc?.slug?.current) return res.status(204).end();
const bodyText = (doc.body || [])
.flatMap((block: any) => block.children?.map((child: any) => child.text) || [])
.join('
');
const documentId = `https://www.example.com/${doc._type}/${doc.slug.current}`;
const endpoint = `https://api.cloud.coveo.com/push/v1/organizations/${process.env.COVEO_ORG_ID}/sources/${process.env.COVEO_SOURCE_ID}/documents?documentId=${encodeURIComponent(documentId)}`;
const response = await fetch(endpoint, {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.COVEO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: doc.title,
data: [doc.excerpt, bodyText].filter(Boolean).join('
'),
metadata: {
sanityid: doc._id,
contenttype: doc._type,
category: doc.category,
publishdate: doc.publishedAt,
clickableuri: documentId,
},
}),
});
if (!response.ok) throw new Error(await response.text());
return res.status(200).json({indexed: documentId});
}How Sanity + Coveo works
Build your Coveo integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to keep Coveo indexed with accurate content.
Start building free →CMS approaches to Coveo
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Structured data for indexing | Often exposes rendered pages, so Coveo may index navigation, duplicate text, and layout content unless you add cleanup rules. | Content Lake stores typed JSON, and GROQ can return a Coveo-ready payload with resolved references and flattened Portable Text. |
| Real-time sync on publish | Often depends on scheduled crawls or manual reindexing, which can leave search results behind editorial changes. | Webhooks or Functions can trigger on content mutations and call the Coveo Push API when content is published, updated, or deleted. |
| Field control for relevance | Search fields may be tied to page templates, which makes it harder to send clean ranking signals like audience, region, and content type. | GROQ can project titles, body text, slugs, taxonomy, locale, and custom ranking fields in one query. |
| Delete and unpublish handling | Deleted pages can remain in the index until the next crawl finds a 404. | Mutation events include document identity, so your handler can call Coveo DELETE with the same canonical documentId used for indexing. |
| Editorial workflow fit | Editors may need to publish first, then wait for a crawl before testing search changes. | Sanity Studio supports draft workflows, Content Releases, scheduled publishing, and custom fields that can control whether content enters Coveo. |
| Trade-offs | Crawling is simpler to start, but precision and freshness are harder to control. | Direct Push API indexing takes planning for schema design, field mappings, and retries, but it gives you clear control over Coveo's index. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Algolia
Build fast site search and autocomplete from structured Sanity content with custom ranking and faceting.
Sanity + Elasticsearch
Index Sanity content into Elasticsearch for custom search, analytics, and internal discovery tools.
Sanity + Yext
Connect structured content to Yext for location, brand, and answers experiences across search surfaces.