How to Integrate VWO with Your Headless CMS
Connect VWO to structured Sanity content so you can test headlines, CTAs, images, and audience-specific variants without copying content by hand.
What is VWO?
VWO is a personalization and experimentation platform for A/B testing, split URL testing, feature experiments, behavioral analytics, and audience targeting. Growth, product, ecommerce, and marketing teams use it to test page changes, feature rollouts, and personalized experiences before shipping them to everyone. In the experimentation market, VWO sits alongside tools like Optimizely, AB Tasty, Dynamic Yield, and LaunchDarkly.
Why integrate VWO with a headless CMS?
Experimentation breaks down fast when content and test setup live in different places. A marketer writes three hero variants in one system, copies the winning headline into VWO, asks a developer to wire up the image, and then repeats the work for mobile, paid landing pages, and regional sites. When the test ends, someone still has to find every copied variant and clean it up.
Architecture overview
A typical Sanity and VWO integration has four parts. First, editors create an experiment document in Sanity Studio with fields like vwoCampaignId, featureKey, variants, audience, locale, and goalId. That document is saved as structured JSON in Content Lake. Second, a GROQ-powered webhook fires when a published experiment changes, for example when a new CTA variant is approved. Third, a Sanity Function or webhook listener fetches the full experiment with GROQ, transforms it into the small payload VWO needs, and calls VWO’s REST API, commonly the VWO API v2 campaign endpoint for campaign metadata or the Feature Management and Experimentation setup your workspace uses. Fourth, the end user visits your site. VWO SmartCode or the VWO Feature Management and Experimentation SDK assigns a variation, your app reads the variation key, fetches the matching content from Sanity, renders it, and sends conversion events back to VWO. This keeps VWO focused on testing and targeting, while Sanity remains the source for content used across web, mobile, and AI agents.
Common use cases
Landing page A/B tests
Test Sanity-authored headlines, hero images, CTA labels, and proof points while VWO handles variation assignment and conversion tracking.
Audience-based personalization
Map VWO audiences such as returning visitors, paid traffic, or account tier to specific Sanity content variants.
Feature experiment content
Use VWO Feature Management and Experimentation to choose a feature variation, then render matching onboarding copy, help text, or promotional modules from Sanity.
Localized experiment variants
Keep localized variant copy in Sanity and let VWO run the same experiment logic across markets without duplicating every page.
Step-by-step integration
- 1
Set up VWO
Create or choose a VWO workspace, note your Account ID, create an API token for VWO’s REST API, and add VWO SmartCode to your site for web tests. For server-side or feature experiments, install the VWO Feature Management and Experimentation SDK, such as vwo-fme-node-sdk, and copy the SDK key from VWO.
- 2
Create the VWO experiment in Sanity Studio
Model a vwoExperiment document with fields such as title, vwoCampaignId, featureKey, goalId, locale, audience, status, and variants. Each variant should include a stable vwoVariationKey, headline, ctaText, image, and any content references your page needs.
- 3
Add a publish trigger
Create a Sanity webhook filtered to published experiment documents, for example _type == "vwoExperiment" && !(_id in path("drafts.**")). Point it at a Sanity Function or your own webhook endpoint. If you use Functions, the sync logic can run on content mutation events without a separate server.
- 4
Fetch only the fields VWO needs
Use GROQ in the webhook handler or Function to fetch the VWO campaign ID, variant keys, short copy, asset URLs, locale, and goal metadata. Don’t send full Portable Text or unrelated editorial fields to VWO unless your rendering strategy needs them.
- 5
Connect to VWO’s API or SDK
Call VWO’s REST API for campaign or feature configuration updates where your VWO plan supports it. At runtime, use VWO SmartCode for browser-based tests or the VWO Feature Management and Experimentation SDK to evaluate a feature key and return the assigned variation.
- 6
Test the full experience
Publish a draft experiment in Sanity, confirm the webhook fires, inspect the VWO API response, and then visit the page with test users or forced variation rules. Check three things: the correct Sanity variant renders, VWO records the visit, and the goal event fires when the user converts.
Code example
Minimal webhook handler that receives a Sanity publish event, fetches experiment content with GROQ, and updates a VWO campaign through VWO’s REST API v2. VWO payload details can vary by campaign type, so verify the variation fields for your workspace.
import express from 'express';
import {createClient} from '@sanity/client';
const app = express();
app.use(express.json());
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,
});
app.post('/webhooks/sanity/vwo', async (req, res) => {
const id = String(req.body._id || req.body.documentId).replace(/^drafts\./, '');
const experiment = await sanity.fetch(`
*[_id == $id][0]{
title,
vwoCampaignId,
variants[]{key, headline, ctaText, "imageUrl": image.asset->url}
}
`, {id});
if (!experiment?.vwoCampaignId) return res.status(204).end();
const vwoRes = await fetch(
`https://app.vwo.com/api/v2/accounts/${process.env.VWO_ACCOUNT_ID}/campaigns/${experiment.vwoCampaignId}`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.VWO_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: experiment.title,
variations: experiment.variants.map((v: any) => ({
name: v.key,
variables: {
headline: v.headline,
ctaText: v.ctaText,
imageUrl: v.imageUrl,
},
})),
}),
}
);
if (!vwoRes.ok) throw new Error(await vwoRes.text());
res.json({synced: true});
});
app.listen(3000);How Sanity + VWO works
Build your VWO integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect VWO experiments with the content your teams publish.
Start building free →CMS approaches to VWO
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Experiment content structure | Variants often become page copies or HTML blocks, so test content is hard to reuse after VWO declares a winner. | Variants, audiences, goals, assets, and references are typed JSON in Content Lake, ready for VWO variable mapping. |
| Publish-triggered sync | An editor publishes content, then someone manually updates VWO or waits for a scheduled export. | GROQ-filtered webhooks or Functions can run on publish and send only changed experiment documents to VWO. |
| Field-level query control | VWO often gets copied snippets, rendered HTML, or page-level data that includes fields it doesn’t need. | One GROQ query can return campaign ID, locale, variation copy, CTA text, image URL, and referenced content in one payload. |
| Runtime personalization | Personalized experiences often split into many page versions that are slow to retire after a test. | Your app asks VWO for the variation key, then renders the matching Sanity content across web, mobile, and other channels. |
| AI and agent readiness | Experiment results, page copy, and content context often sit in separate systems with limited structured access. | Agent Context can provide scoped, read-only access to winning content, test metadata, references, and approved messaging. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Optimizely
Connect structured Sanity content to Optimizely experiments, feature flags, and personalized digital experiences.
Sanity + LaunchDarkly
Pair LaunchDarkly feature flags with Sanity-authored onboarding copy, release notes, and in-product messaging.
Sanity + Dynamic Yield
Use Sanity product, editorial, and campaign content inside Dynamic Yield personalization programs.