Marketing & Advertising8 min read

How to Integrate Customer.io with Your Headless CMS

Connect Customer.io to your headless CMS so published offers, newsletter modules, and audience-specific content can trigger email, SMS, push, and in-app campaigns without copy-paste.

Published April 29, 2026
01 โ€” Overview

What is Customer.io?

Customer.io is a customer engagement platform for sending behavior-based email, SMS, push, in-app, and webhook messages. Marketing, growth, and product teams use it to build customer journeys from profile attributes, events, segments, and campaign data. It sits in the marketing automation category alongside tools like Braze, Iterable, and Salesforce Marketing Cloud, with a strong focus on event-triggered messaging and data flexibility.


02 โ€” The case for integration

Why integrate Customer.io with a headless CMS?

Marketing campaigns break down when content lives in one place and customer journeys live somewhere else. A product launch page might be updated in your content system at 10:00 AM, while the Customer.io email still has last week's headline, expired pricing, or the wrong CTA. Someone ends up copying fields into Customer.io by hand, and every manual step adds delay and risk.


03 โ€” Architecture

Architecture overview

A typical flow starts when an editor publishes campaign content in Sanity Studio. A Sanity webhook fires on the publish mutation, or a Sanity Function runs server-side from the same content event. The handler receives the document ID, uses GROQ to fetch only the fields required for the campaign, and sends those fields to Customer.io. For one-to-one lifecycle messages, you can call Customer.io's Track API through the customerio-node SDK and attach the content payload to an event. For a campaign sent to a segment, you can call Customer.io's App API, such as the API-triggered broadcast endpoint, with the campaign data and recipient segment. Customer.io then uses that payload in Journeys, newsletters, Liquid variables, email templates, SMS, push, in-app messages, or downstream webhooks. The end user receives a message that reflects the same structured content powering your site, app, and other channels.


04 โ€” Use cases

Common use cases

๐Ÿ“ฃ

Launch announcements

Publish a product announcement in Sanity, then trigger a Customer.io campaign with the headline, hero image, CTA, and target segment.

๐Ÿงช

A/B campaign content

Model subject lines, preview text, CTAs, and content variants in Sanity, then pass the selected variant into Customer.io experiments.

๐ŸŒŽ

Localized lifecycle messages

Use Sanity locale fields to send Customer.io the right copy, image, and link for each market without duplicating campaign setup.

๐Ÿ›’

Behavior-based recommendations

Combine Customer.io events with Sanity product, guide, or offer content so abandoned-cart, trial, and renewal messages use current copy.


05 โ€” Implementation

Step-by-step integration

  1. 1

    Set up Customer.io access

    Create or open your Customer.io workspace, then copy your Site ID and Track API Key from Workspace Settings. If you're triggering broadcasts or campaigns from the App API, create an App API key and note your region, such as US or EU.

  2. 2

    Install the packages

    In your webhook handler or middleware project, install @sanity/client and customerio-node. Keep SANITY_PROJECT_ID, SANITY_DATASET, SANITY_READ_TOKEN, CUSTOMERIO_SITE_ID, and CUSTOMERIO_TRACK_API_KEY in environment variables.

  3. 3

    Model campaign content in Sanity Studio

    Create schema fields for campaignTitle, summary, image, ctaText, ctaUrl, locale, audienceKey, customerIoSegmentId, and publishedAt. Use references for products, categories, or offers so GROQ can join the related data into one payload.

  4. 4

    Create the sync trigger

    Add a Sanity webhook filtered to published campaign documents, for example _type == "campaignMessage" && !(_id in path("drafts.**")). Send a compact payload with the document ID to a Sanity Function, Next.js route, serverless function, or your own webhook listener.

  5. 5

    Send the content to Customer.io

    In the handler, fetch the full document from the Content Lake with GROQ, map Sanity fields to Customer.io event data or broadcast data, then call the Track API or App API. Add retries for 429 and 5xx responses, and log the Sanity document ID with the Customer.io request ID when available.

  6. 6

    Test the end-to-end experience

    Publish a test document in Sanity, confirm the webhook fired, inspect the Customer.io event or API-triggered campaign, and send to a test profile. Check Liquid variables, links, UTM values, images, localization, and unsubscribe behavior before sending to a real segment.


06 โ€” Code

Code example

tsapp/api/sanity-customerio/route.ts
import { createClient } from "@sanity/client";
import { CustomerIO } from "customerio-node";

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

const cio = new CustomerIO(
  process.env.CUSTOMERIO_SITE_ID!,
  process.env.CUSTOMERIO_TRACK_API_KEY!
);

export async function POST(req: Request) {
  const { _id, customerId } = await req.json();

  const campaign = await sanity.fetch(
    `*[_id == $id][0]{
      _id,
      campaignTitle,
      summary,
      ctaText,
      ctaUrl,
      locale,
      "imageUrl": image.asset->url,
      "product": product->{name, slug}
    }`,
    { id: _id.replace("drafts.", "") }
  );

  await cio.track(customerId, {
    name: "sanity_campaign_published",
    data: {
      contentId: campaign._id,
      title: campaign.campaignTitle,
      summary: campaign.summary,
      ctaText: campaign.ctaText,
      ctaUrl: campaign.ctaUrl,
      imageUrl: campaign.imageUrl,
      locale: campaign.locale,
      productName: campaign.product?.name
    }
  });

  return Response.json({ ok: true });
}

07 โ€” Why Sanity

How Sanity + Customer.io works

Build your Customer.io integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Customer.io campaigns with the content your teams publish every day.

Start building free โ†’

08 โ€” Comparison

CMS approaches to Customer.io

CapabilityTraditional CMSSanity
Campaign content structureCampaign copy often lives inside pages or rich text blocks, so teams may need to copy fields into Customer.io manually.Schemas in code let you model Customer.io-ready fields, such as audienceKey, customerIoSegmentId, locale, CTA, and offer references.
Real-time sync on publishUpdates may require scheduled exports, plugins, or manual handoffs before Customer.io has current content.Webhooks and Functions can react to content mutations and send data to Customer.io without a separate job server.
Field-level payload controlIntegrations may receive full pages, rendered HTML, or fields that need cleanup before use in messages.GROQ can fetch exactly the fields Customer.io needs, including referenced products, offers, categories, and image URLs in one query.
Localization for campaignsLocalized email content is often duplicated across pages, spreadsheets, and campaign builders.Editors can work with localized fields in Sanity Studio, then the integration can send locale-specific payloads to Customer.io.
AI content operationsAI-assisted updates often happen outside the editorial source, which can create review and governance gaps.Content Agent and Agent API can help audit, transform, and translate structured campaign content before it reaches Customer.io.

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 Customer.io and 200+ other tools.