AACsearch
Connectors & Widget

Headless CMS Integrations

How AACsearch integrates with headless CMS platforms — Strapi, Sanity, Contentful, Directus and Payload — and when to choose a plugin vs an integration guide.

Headless CMS platforms expose content via API rather than rendering pages. AACsearch supports them through two patterns:

  • Plugin — code that runs inside the CMS process, listens to lifecycle hooks, and pushes deltas through the AACsearch Connector API. Strapi and (planned) Directus / Payload fit this pattern.
  • Integration guide — no plugin; the CMS already publishes webhooks or change streams that you wire to AACsearch via a small adapter. Contentful and Sanity fit this pattern.

This page is the umbrella. Each platform has its own dedicated guide linked below.

Pattern matrix

PlatformPatternStatusDedicated page
Strapi 5.xnpm pluginBetaStrapi
Sanity.ioGROQ + listenerIntegration guideSanity
ContentfulWebhook adapterIntegration guideContentful
Directus 10+npm extensionPlanned
Payload CMS 2+Plugin (collections)Planned
Generic / otherCustom connectorCustom Connector

When to use a plugin

Pick the plugin path when the CMS has a stable extension API and runs as a long-lived process. The plugin gives you:

  • Real-time hooks on every create / update / delete with the entity payload in scope, so there is no second API round trip to fetch the saved entity.
  • An admin settings panel embedded in the CMS UI for the merchant or editor to manage the connector token.
  • Localized content via the platform's native locale primitives, mapped into the AACsearch locale field.

The Strapi plugin under packages/strapi-plugin/ is the reference implementation. It uses Strapi's lifecycles API (afterCreate, afterUpdate, afterDelete) to call enqueueDelta, plus a bootstrap hook to run an opt-in full sync at startup. The plugin source is reusable as a template for Directus and Payload — same lifecycle pattern, different host APIs.

When to use an integration guide

Pick the integration guide path when:

  • The CMS is SaaS-hosted and you cannot run plugin code inside it (Contentful, Sanity).
  • The CMS exposes a robust webhook or change-stream API that already includes the entity payload.
  • You only need to index a subset of content types and adding a plugin is over-engineering for the use case.

For these platforms, the AACsearch docs describe how to:

  1. Subscribe to the CMS's webhook or change feed.
  2. Configure a small adapter (a serverless function, a webhook receiver in your existing API, or a Zapier zap) that maps the payload to SyncProductInput.
  3. Forward the delta to POST /api/projects/:projectId/sync/delta.

See Contentful and Sanity for the working recipes.

Choosing your content model

Headless CMS content rarely fits the e-commerce product shape. The shared document schema accepts any field structure under attributes, so the mapping question becomes:

  • Searchable fieldstitle, description. Always send plain text after stripping markdown or rich-text JSON.
  • Faceted filtersattributes.<key> with the value as the appropriate primitive type. AACsearch infers the facet type from the first batch.
  • Display fieldsattributes.<key> for anything you only need on the result card, not in the search query.
  • URLs and imagesproduct_url, image_url. The widget renders these by default.
  • Locale → the CMS's native locale, narrowed to en | de | es | fr | ru (AACsearch supports those five). If your content is in another language, send the closest neighbor and store the actual code in attributes.language.

For non-product entities (articles, recipes, FAQ entries), reuse the same schema:

{
  "external_id": "article-2451",
  "title": "How to brew a pour-over",
  "description": "A short guide…",
  "categories": ["Guides", "Coffee"],
  "tags": ["beginner", "pour-over"],
  "image_url": "https://cms.example.com/img/pour-over.jpg",
  "product_url": "https://site.example.com/guides/pour-over",
  "locale": "en",
  "attributes": {
    "type": "article",
    "author": "Jane Doe",
    "published_at": "2025-01-12",
    "read_time_minutes": 4
  }
}

Use attributes.type to distinguish content types in a single index, or create separate indexes per type if the merchant wants independent ranking and analytics.

Multi-locale strategy

Headless CMS platforms typically model locales explicitly. The AACsearch convention is one document per locale per entity:

  • external_id is suffixed by locale: article-2451:en, article-2451:de.
  • The widget passes locale from the storefront so users only see results in their language.
  • Updates to one locale do not affect documents for other locales — your event handler should emit only the locale that changed.

See Strapi for the canonical implementation of this pattern.

On this page