How to Integrate Marketo with Your Headless CMS
Connect Marketo to structured content so campaign teams can publish landing pages, email modules, forms, and nurture content without copy-pasting between systems.
What is Marketo?
Adobe Marketo Engage is a marketing automation platform used for lead capture, email marketing, nurture programs, scoring, segmentation, and campaign reporting. It’s common in B2B and enterprise marketing teams that run multi-touch campaigns across web forms, landing pages, email, paid media, and sales handoff. Marketo’s core strength is turning lead behavior into automated campaign actions, such as adding someone to a Smart Campaign after they submit a form or click a tracked email link.
Why integrate Marketo with a headless CMS?
If your campaign content lives in one system and Marketo lives in another, teams usually end up copying subject lines, email body modules, CTA text, form IDs, UTM parameters, and landing page copy by hand. That works for one campaign. It breaks down when you’re shipping 40 regional landing pages, 12 nurture emails, and three audience variants with legal review attached to each field.
Architecture overview
A typical Sanity and Marketo integration starts when an editor publishes a campaign document in Sanity Studio. A webhook filtered with GROQ, for example `_type == "marketoEmail" && defined(marketo.emailId)`, fires only for documents that should sync to Marketo. The webhook sends the document ID to a Sanity Function, which fetches the published content from the Content Lake with GROQ, including referenced offer, audience, and CTA data. The Function requests an OAuth access token from Marketo’s Identity Endpoint using the client ID and client secret from a LaunchPoint service. It then calls Marketo’s REST Asset API, such as `POST /rest/asset/v1/email/{id}/content/{htmlId}.json`, to update an editable email section, or it can call Lead API endpoints when the integration is capturing or enriching lead data. After Marketo updates the asset, your frontend can render the same Sanity content on a landing page, embed a Marketo Form 2.0 form, and let Marketo handle lead capture, scoring, Smart Campaigns, and email delivery.
Common use cases
Sync approved email modules
Publish a Sanity campaign email and update the matching editable section in a Marketo email asset using its email ID and HTML module ID.
Run gated content campaigns
Use Sanity for the landing page and offer content, then embed a Marketo form that captures leads and starts the right Smart Campaign.
Personalize nurture paths
Map Sanity audience segments and offer metadata to Marketo programs so different leads receive the right follow-up emails.
Localize campaign assets
Publish regional campaign variants from Sanity and sync localized subject lines, CTAs, and landing page copy into Marketo assets.
Step-by-step integration
- 1
Set up Marketo API access
In Marketo Admin, create a LaunchPoint service with API access, then copy the client ID, client secret, Identity Endpoint, and REST Endpoint from Admin > Web Services. Make sure the API user has access to the Asset API for email or landing page updates, and the Lead API if you’re syncing lead fields.
- 2
Prepare the Marketo assets
Create the Marketo program, email, landing page, or form you want to connect. For email syncing, add an editable module and note the email asset ID and the module HTML ID, because the REST Asset API needs both values when updating content.
- 3
Model campaign content in Sanity Studio
Create a schema for campaign content with fields like `subject`, `preheader`, `bodyHtml`, `ctaLabel`, `ctaUrl`, `audienceSegment`, and a `marketo` object containing `emailId`, `htmlId`, `programId`, or `formId`. Add validation so editors can’t publish a Marketo-synced document without the required IDs.
- 4
Create the sync trigger
Add a Sanity webhook or Sanity Function that runs on publish events. Use a GROQ filter such as `_type == "marketoEmail" && defined(marketo.emailId)` so the integration only runs for documents that are meant to update Marketo.
- 5
Call Marketo’s REST API
Inside the Function or webhook handler, fetch the published Sanity document with `@sanity/client`, request a Marketo OAuth token, then call the correct Marketo endpoint. For email content, use `POST /rest/asset/v1/email/{id}/content/{htmlId}.json` with `type=HTML` and `value=<your HTML>`.
- 6
Test the campaign flow
Preview the Sanity-powered landing page, submit a Marketo form, confirm the lead appears in Marketo, check that the Smart Campaign triggers, and send a Marketo email preview to verify the synced module content. Also test failure cases, such as an expired token, missing module ID, or Marketo API quota error.
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_TOKEN,
useCdn: false,
})
async function getMarketoToken() {
const qs = new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.MARKETO_CLIENT_ID!,
client_secret: process.env.MARKETO_CLIENT_SECRET!,
})
const res = await fetch(`${process.env.MARKETO_IDENTITY_URL}/oauth/token?${qs}`)
if (!res.ok) throw new Error(`Marketo auth failed: ${res.status}`)
return (await res.json()).access_token as string
}
export default async function handler(req: Request) {
const {_id} = await req.json()
const doc = await sanity.fetch(
`*[_id == $id][0]{
subject,
preheader,
bodyHtml,
marketo{emailId, htmlId}
}`,
{id: _id}
)
if (!doc?.marketo?.emailId || !doc?.marketo?.htmlId) {
return Response.json({ok: false, reason: 'Missing Marketo mapping'}, {status: 400})
}
const token = await getMarketoToken()
const html = `<h1>${doc.subject}</h1><p>${doc.preheader}</p>${doc.bodyHtml}`
const body = new URLSearchParams({type: 'HTML', value: html})
const url = `${process.env.MARKETO_REST_URL}/asset/v1/email/${doc.marketo.emailId}/content/${doc.marketo.htmlId}.json?access_token=${token}`
const update = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body,
})
if (!update.ok) throw new Error(`Marketo update failed: ${update.status}`)
return Response.json({ok: true})
}How Sanity + Marketo works
Build your Marketo integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect campaign content with Marketo.
Start building free →CMS approaches to Marketo
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Campaign content structure | Campaign copy is often tied to pages, HTML fields, or templates, which makes reuse in Marketo email modules harder. | Schemas can model Marketo-specific fields, such as email ID, module HTML ID, form ID, program ID, audience segment, and CTA data. |
| Sync on publish | Teams often copy approved copy into Marketo manually or run scheduled exports. | Webhooks and Functions can run server-side sync logic on publish events without separate integration infrastructure. |
| Field-level data control | Marketo integrations may receive full page payloads or rendered HTML, then parse out the useful parts. | GROQ can fetch exactly the fields Marketo needs, including joins across references, in one query. |
| Editorial safeguards | Editors may not see whether a page field maps to a Marketo asset until the campaign is tested. | Sanity Studio can validate required Marketo IDs before publish and show campaign teams the fields that sync downstream. |
| Multi-channel campaign reuse | Website, email, and paid media copy are commonly duplicated across tools. | One structured back end can feed web pages, Marketo emails, mobile content, paid campaign metadata, and AI agents. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Google Ads
Send structured campaign copy, landing page metadata, and UTM-ready URLs into paid search workflows.
Sanity + HubSpot
Connect forms, lead magnets, lifecycle content, and landing pages to HubSpot marketing and CRM workflows.
Sanity + Salesforce Marketing Cloud
Feed structured email, journey, and personalization content into Salesforce Marketing Cloud campaigns.