How to Integrate Iterable with Your Headless CMS
Connect Iterable to a headless CMS so campaign, product, and editorial content can update email, SMS, push, and in-app messages from one structured source.
What is Iterable?
Iterable is a cross-channel marketing platform for lifecycle teams that run email, SMS, mobile push, in-app, and web push campaigns. Teams use it to build journeys, segment audiences, personalize messages, and measure engagement across customer touchpoints. It’s commonly used by growth, CRM, and marketing operations teams at consumer apps, retailers, marketplaces, and subscription businesses.
Why integrate Iterable with a headless CMS?
Marketing teams usually don’t struggle because Iterable is hard to send from. They struggle because the content feeding those sends is copied from five places. A product launch might need a landing page headline, an email subject line, a push notification, a promo code, a localized hero image, and legal copy. If those fields live in spreadsheets, tickets, and pasted HTML, every campaign becomes a manual QA job.,Connecting Iterable to a headless CMS category tool fixes the handoff between content and lifecycle marketing. With Sanity as the AI Content Operating System, campaign content is structured as typed JSON in the Content Lake. That means Iterable can receive exactly the fields it needs, such as title, summary, CTA URL, image URL, locale, segment tags, expiration date, and product references. No HTML scraping. No parsing page blobs. No waiting for a nightly CSV export.,The alternative is slower and riskier. Someone copies a product description into an Iterable template, another person updates the website, and a third person edits the mobile push copy. Two days later, the price changes. Now you’ve got inconsistent messages in market. With real-time webhooks and Functions, a publish event in Sanity can update an Iterable Catalog item in seconds, so the next triggered campaign uses the current approved content.
Architecture overview
A typical Sanity and Iterable integration starts when an editor publishes a campaign, product, article, or promotion document in Sanity Studio. The published document lands in the Content Lake as structured JSON. A GROQ-powered webhook fires only for the content types you care about, for example, _type == "promotion" or _type == "product". The webhook calls a Sanity Function or your own HTTPS listener with the document ID and mutation details. The server-side handler uses @sanity/client and GROQ to fetch the exact fields Iterable needs, including joined data such as product category, image asset URL, localized copy, and campaign metadata. The handler then calls Iterable’s REST API, usually the Catalogs API for reusable campaign content, such as PUT /api/catalogs/{catalogName}/items/{itemId}. For user-level events, it can also call POST /api/events/track. Iterable templates, journeys, and triggered campaigns can reference that catalog content when sending email, SMS, push, or in-app messages to the end user. Functions are a good fit when you don’t want to run a separate server for this sync logic. If you already have middleware for customer data, you can route the webhook there instead.
Common use cases
Launch campaigns from approved content
Publish a promotion in Sanity and sync the headline, offer copy, CTA, image URL, and expiration date to an Iterable Catalog used by email and push campaigns.
Personalized product recommendations
Send product names, descriptions, categories, image assets, and landing URLs from Sanity into Iterable Catalogs so templates can render current product content.
Localized lifecycle messaging
Sync locale-specific copy from Sanity to Iterable fields like en-US, fr-FR, and de-DE so regional campaigns don’t depend on copied spreadsheet text.
Time-sensitive offer updates
Update promo codes, legal disclaimers, sale end dates, and landing page links in Sanity, then push the change to Iterable before the next triggered send.
Step-by-step integration
- 1
Set up Iterable access
In Iterable, create or choose the project that will receive content. Generate a server-side API key with permission to update Catalogs and, if needed, send custom events. Confirm whether your project uses the US API base URL, https://api.iterable.com, or the EU base URL, https://api.eu.iterable.com. Create a catalog such as content_promos or products if you’ll reference synced content in templates.
- 2
Model the campaign content in Sanity Studio
Create schema fields that match how lifecycle marketers actually work. For a promotion, include title, slug, summary, heroImage, ctaUrl, promoCode, locale, segmentTags, expiresAt, and iterableCatalogName. If campaigns reference products, model those as references instead of duplicating product data in every promotion.
- 3
Write the GROQ query
Use GROQ to fetch only the fields Iterable needs. A promotion sync might project title, slug.current, summary, image asset URL, CTA URL, expiration date, locale, and referenced product fields. This keeps the payload small and prevents draft-only editorial notes from being sent to Iterable.
- 4
Create the sync trigger
Add a Sanity webhook filtered to published mutations for the content types you want to sync, or use a Sanity Function for server-side logic triggered by content changes. A typical filter is _type in ["promotion", "product"] so unrelated edits, such as internal documentation, don’t call Iterable.
- 5
Push content into Iterable
From the Function or webhook listener, call Iterable’s Catalogs API with the API key in the Api-Key header. Use a stable item ID, such as the Sanity document ID or slug, so later publishes update the same Iterable item instead of creating duplicates.
- 6
Test the campaign path
Publish a test document in Sanity, confirm the catalog item appears in Iterable, reference it in an email or push template, and send a test message to an internal user. Also test edits, unpublishes, missing images, expired offers, and locale fallback behavior.
Code example
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 default async function handler(req: Request) {
const { _id } = await req.json()
const promo = await sanity.fetch(`*[_id == $id][0]{
_id,
title,
summary,
locale,
ctaUrl,
expiresAt,
"slug": slug.current,
"imageUrl": heroImage.asset->url
}`, {id: _id.replace('drafts.', '')})
if (!promo) return new Response('No published document', {status: 202})
const itemId = encodeURIComponent(promo.slug || promo._id)
const baseUrl = process.env.ITERABLE_API_BASE || 'https://api.iterable.com'
const iterableRes = await fetch(
`${baseUrl}/api/catalogs/content_promos/items/${itemId}`,
{
method: 'PUT',
headers: {
'Api-Key': process.env.ITERABLE_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
value: {
title: promo.title,
summary: promo.summary,
locale: promo.locale || 'en-US',
imageUrl: promo.imageUrl,
ctaUrl: promo.ctaUrl,
expiresAt: promo.expiresAt,
},
}),
}
)
if (!iterableRes.ok) {
throw new Error(`Iterable sync failed: ${iterableRes.status}`)
}
return new Response('Synced to Iterable', {status: 200})
}How Sanity + Iterable works
Build your Iterable integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect approved content with Iterable campaigns.
Start building free →CMS approaches to Iterable
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Campaign content structure | Campaign copy often lives inside pages, making it hard to reuse the same offer in email, SMS, push, and in-app messages. | Sanity Studio schemas can model promotion-specific fields like promoCode, locale, CTA URL, segment tags, legal copy, and expiration date. |
| Iterable catalog sync | Teams often export CSV files or copy content into Iterable by hand, which creates stale offers and duplicate work. | Webhooks and Functions can push published content directly to Iterable Catalogs without polling or a scheduled batch job. |
| Field-level control | APIs may return rendered pages or large content objects, so the integration has to filter out layout and internal fields. | GROQ can fetch the exact Iterable payload, including joined references for products, categories, images, and localized content. |
| Marketing team workflow | Editors can publish pages, but lifecycle-specific approval steps often move to tickets, spreadsheets, or chat. | Sanity Studio supports custom workflows with Comments, Tasks, Content Releases, and scheduled publishing for campaign content. |
| Multi-channel reuse | Website pages are usually the main output, and other channels get copied versions of the same content. | One structured back end can feed websites, apps, Iterable, Live Content API subscribers, and AI agents through Agent Context. |
| Setup trade-off | Initial setup can feel familiar for editors, but integrations often become manual once campaigns span multiple channels. | You’ll spend more time designing the schema and sync rules, but you get cleaner payloads and fewer manual campaign updates later. |
Keep building
Explore related integrations to complete your content stack.
Sanity + HubSpot
Connect structured content with HubSpot forms, CRM records, and nurture programs for lead generation workflows.
Sanity + Mailchimp
Send approved campaign copy, product updates, and audience-specific content from Sanity into Mailchimp email programs.
Sanity + Salesforce Marketing Cloud
Use Sanity as the structured content source for large-scale email, mobile, and customer journey programs in Salesforce Marketing Cloud.