Personalization & Experimentation8 min read

How to Integrate Optimizely with Your Headless CMS

Connect Optimizely to structured content so teams can test headlines, offers, pages, and audience-specific experiences without copying content between tools.

Published April 29, 2026
01 — Overview

What is Optimizely?

Optimizely is a digital experience platform used for web experimentation, feature experimentation, personalization, and digital experience analytics. Product, growth, marketing, and engineering teams use Optimizely to run A/B tests, route traffic, target audiences, and measure conversions across websites and apps. Its experimentation tools are widely used by mid-market and enterprise teams that need controlled rollouts and measurable content or product changes.


02 — The case for integration

Why integrate Optimizely with a headless CMS?

Experimentation gets messy when content lives in one place and test logic lives somewhere else. A landing page headline changes in your content system, but the A/B test in Optimizely still points to last week’s copy. A personalization campaign targets “enterprise visitors,” but the actual offer text is buried in a page field that nobody wants to paste into another UI.

Connecting Optimizely to a headless CMS category system solves that split. With Sanity’s AI Content Operating System, content is structured as typed JSON in the Content Lake, so Optimizely can receive specific fields like headline, CTA label, audience key, slug, experiment key, and variation ID. GROQ selects the exact payload, webhooks fire on publish, and Functions can run the sync logic without a separate worker app.

The alternative is manual work. Someone copies variation copy into Optimizely, another person updates the website, and a third person checks whether the experiment still matches the published page. That can work for one test. It breaks down when you’re running 20 campaigns across 6 locales, 4 audience segments, and multiple frontend surfaces.


03 — Architecture

Architecture overview

A typical Sanity and Optimizely integration starts with structured content in the Content Lake. Editors create experiment-ready documents in Sanity Studio, for example a campaign with references to variants, audience segments, locale-specific copy, and Optimizely IDs. When a document is published, a Sanity webhook can fire with a GROQ filter such as _type == "experimentVariant" && defined(optimizely.featureId). The webhook calls a Sanity Function or your own middleware. That server-side code fetches the published document from the Content Lake using GROQ, joins referenced audience and campaign documents, maps the result to Optimizely fields, and calls the Optimizely REST API with a bearer token. For Optimizely Feature Experimentation, the sync often updates a feature variable or variation metadata with a Sanity content ID, headline, CTA, or JSON payload. For Optimizely Web Experimentation, the sync may update experiment or variation configuration while the page still renders content from Sanity. At runtime, the frontend uses the Optimizely Web snippet or Feature Experimentation SDK to decide which variation a visitor should see, then renders the matching Sanity content to the end user.


04 — Use cases

Common use cases

đź§Ş

A/B test Sanity-authored page copy

Editors create headline, body, image, and CTA variants in Sanity, while Optimizely assigns traffic and measures conversions.

🎯

Personalize offers by audience

Sync audience keys and offer IDs from Sanity to Optimizely so campaigns can target segments like industry, region, plan type, or lifecycle stage.

đźš©

Run feature-flagged content launches

Use Optimizely Feature Experimentation to decide when a visitor sees a new promo, pricing message, or onboarding flow backed by Sanity content.

🌎

Test localized campaigns

Publish locale-specific variants in Sanity and map them to Optimizely experiments for markets such as en-US, fr-FR, and de-DE.


05 — Implementation

Step-by-step integration

  1. 1

    Set up Optimizely

    Create an Optimizely Web Experimentation or Feature Experimentation project, add your environments, and copy the project ID, SDK key, and REST API personal access token. If you’re using Feature Experimentation, create a flag and note the feature ID, variable ID, and variation keys you’ll map to Sanity documents.

  2. 2

    Install the required packages

    In your sync service or Sanity Function, install @sanity/client for Content Lake reads. In your frontend, install @optimizely/optimizely-sdk if you’re using Feature Experimentation decisioning, or add the Optimizely Web snippet if you’re running browser-based experiments.

  3. 3

    Model experiment content in Sanity Studio

    Create schema fields for experimentKey, featureId, variableId, variationKey, audienceKey, locale, headline, image, CTA label, CTA URL, and status. Keep Optimizely IDs close to the content so editors can see which campaign or flag a variant feeds.

  4. 4

    Create the publish trigger

    Add a Sanity webhook with a GROQ filter such as _type == "experimentVariant" && defined(optimizely.featureId). Send published mutations to a Sanity Function or webhook listener, and use a shared secret to verify the request.

  5. 5

    Call the Optimizely API

    Fetch the full Sanity document with GROQ, map fields into the shape your Optimizely flag, variable, or experiment expects, and call the Optimizely REST API using a bearer token. Keep large rich text bodies in Sanity when possible, and send content IDs or short JSON values to Optimizely.

  6. 6

    Test the frontend experience

    Use Optimizely’s preview or forced-variation tools to check each variant. Then confirm the frontend decision flow: Optimizely returns a variation key, your app fetches the matching Sanity content, and analytics events record the conversion you care about.


06 — Code

Code example

A small webhook handler that receives a Sanity publish event, fetches the current content with GROQ, and updates an Optimizely Feature Experimentation variable through the Optimizely REST API.

typescriptsync-sanity-to-optimizely.ts
import { createClient } from '@sanity/client'

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
})

export async function POST(req: Request) {
  const event = await req.json()
  const id = event._id.replace('drafts.', '')

  const variant = await sanity.fetch(`
    *[_id == $id][0]{
      _id,
      headline,
      ctaLabel,
      ctaUrl,
      variationKey,
      optimizely{featureId, variableId}
    }
  `, { id })

  if (!variant?.optimizely?.featureId) {
    return Response.json({ skipped: true })
  }

  const value = JSON.stringify({
    sanityId: variant._id,
    variationKey: variant.variationKey,
    headline: variant.headline,
    ctaLabel: variant.ctaLabel,
    ctaUrl: variant.ctaUrl
  })

  const res = await fetch(
    `https://api.optimizely.com/v2/features/${variant.optimizely.featureId}/variables/${variant.optimizely.variableId}`,
    {
      method: 'PATCH',
      headers: {
        Authorization: `Bearer ${process.env.OPTIMIZELY_TOKEN}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ default_value: value })
    }
  )

  if (!res.ok) {
    throw new Error(`Optimizely API error: ${res.status} ${await res.text()}`)
  }

  return Response.json({ synced: variant._id })
}

07 — Why Sanity

How Sanity + Optimizely works

Build your Optimizely integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect Optimizely experiments with content your teams can actually operate at scale with AI.

Start building free →

08 — Comparison

CMS approaches to Optimizely

CapabilityTraditional CMSSanity
Experiment-ready content structureVariants often live as duplicated pages or HTML blocks, which makes field-level testing harder.Schemas define variants, audiences, locales, and Optimizely IDs as typed fields in the Content Lake.
Sync on publishTeams often copy content into Optimizely manually or export updates on a schedule.Webhooks and Functions can trigger sync logic when content changes, with no polling loop.
Field-level payload controlAPIs may return page-shaped content, including fields Optimizely doesn’t need.GROQ can return exactly the experiment payload, including joined references, in one query.
Editorial workflow for test variantsEditors may create duplicate pages for each test, which increases cleanup work after the winner is chosen.Sanity Studio can show experiment fields, previews, tasks, comments, and release planning in one editing workspace.
Runtime personalizationThe website is often the main delivery target, so reuse across apps and agents takes extra work.One structured back end can feed web, mobile, Optimizely, and AI agents through APIs and Agent Context.

09 — Next 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 Optimizely and 200+ other tools.