Search8 min read

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.

Published April 29, 2026
01Overview

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.


02The case for integration

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.


03Architecture

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.


04Use cases

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.


05Implementation

Step-by-step integration

  1. 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. 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. 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. 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. 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. 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.


06Code

Code example

typescriptapi/sanity-to-coveo.ts
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});
}

07Why Sanity

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 →

08Comparison

CMS approaches to Coveo

CapabilityTraditional CMSSanity
Structured data for indexingOften 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 publishOften 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 relevanceSearch 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 handlingDeleted 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 fitEditors 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-offsCrawling 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.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Coveo and 200+ other tools.