How to Integrate Checkly with Your Headless CMS
Connect Checkly to your headless CMS so every published page, locale, and API-backed route can create or update its own synthetic monitor.
What is Checkly?
Checkly is a developer-first synthetic monitoring platform for API checks, browser checks, multistep flows, and monitoring as code. Teams use it to run Playwright-based checks from global locations, alert on failures, and catch broken production paths before users report them. It’s used by engineering, SRE, DevOps, and platform teams that want monitoring definitions to live close to application code.
Why integrate Checkly with a headless CMS?
Content changes break production in ways uptime checks often miss. A campaign page can publish with a missing hero image, a localized slug can change without a redirect, or a product detail page can ship with an empty price component. Checkly can test those paths with browser checks and API checks, but it needs to know which URLs matter, what text or selector should appear, and when those routes change.
Connecting Checkly to a headless CMS turns published content into monitoring input. Instead of asking an engineer to manually add a check after every new landing page, docs section, or locale launch, the content event can create or update a Checkly check automatically. With Sanity, structured content in the Content Lake gives you typed fields like slug, locale, expectedSelector, and monitoringFrequency. GROQ selects only those fields, and webhooks or Functions run the sync as soon as content is published.
The disconnected alternative is slower and noisier. Someone exports URLs to a spreadsheet, copies them into Checkly, forgets to remove retired pages, and alerts start firing for content that no longer exists. The trade-off is that you’ll need clear rules for which content deserves a monitor. Creating a 1-minute browser check for every archived page can create alert fatigue and cost more than it’s worth.
Architecture overview
A typical Sanity and Checkly integration starts with a content document in the Content Lake, such as a landing page, docs article, pricing page, or product route. The document includes fields that monitoring needs: slug, locale, canonical URL, expected text, expected selector, check frequency, and Checkly locations. When the document is published, updated, or deleted, a Sanity webhook fires. The webhook can call a Sanity Function or your own API route. That handler uses @sanity/client and a GROQ query to fetch the current published document, including joined data like parent route, market, and release status. The handler then calls Checkly’s REST API at https://api.checklyhq.com/v1/checks with an API key and X-Checkly-Account header. For a published page, the handler creates or updates a Checkly browser check with a Playwright script that visits the route and asserts that the expected content renders. For an API-backed route, it can create an API check with request assertions. For deleted or unpublished content, it can mute or delete the matching Checkly check. The end result is practical: editors publish content, Checkly starts testing the live path, and your team gets alerted in Slack, PagerDuty, email, or your configured Checkly channel if the content route fails.
Common use cases
Monitor new campaign pages on publish
Create a Checkly browser check when a Sanity landing page goes live, then verify the page returns 200 and contains the expected headline.
Test localized routes
Generate separate checks for en-US, de-DE, and fr-FR slugs so changed locale paths don’t silently break regional pages.
Validate content-backed APIs
Use Sanity fields to define API endpoints and expected JSON values, then create Checkly API checks for those routes.
Retire stale monitors automatically
When content is unpublished or archived, mute or delete the related Checkly check so old URLs don’t create false alarms.
Step-by-step integration
- 1
Set up Checkly access
Create a Checkly account, open Account settings, create a user API key, and copy your account ID. Store them as CHECKLY_API_KEY and CHECKLY_ACCOUNT_ID in your hosting platform or Sanity Function environment. If you manage checks as code too, install the Checkly CLI with npm i -D @checkly/cli.
- 2
Model monitoring fields in Sanity Studio
Add fields to the relevant schema, such as enableMonitoring, expectedText, expectedSelector, checkFrequency, checkLocations, and monitorType. Keep these fields editor-friendly. For example, let editors pick 10-minute or 30-minute frequency instead of typing raw Checkly values.
- 3
Create a publish webhook
In Sanity, create a webhook for the document types you want to monitor, such as page, article, or product. Trigger it on create, update, and delete mutations. Send at least _id, _type, and the transition state so your handler can decide whether to create, update, mute, or delete a Checkly check.
- 4
Fetch exact content with GROQ
In your webhook handler or Sanity Function, use @sanity/client to fetch only the fields Checkly needs. GROQ can join references, build full paths from parent pages, and skip drafts so checks are based on published content only.
- 5
Call the Checkly API
Use Checkly’s REST API to create or update a browser check or API check. Send Authorization: Bearer <api_key> and X-Checkly-Account: <account_id>. Use a stable tag like sanity:<documentId> so future publishes update the existing check instead of creating duplicates.
- 6
Test the frontend path and alert flow
Publish a test page in Sanity Studio, confirm the Checkly check appears, run it manually, and verify the alert channel. Then test an unpublished page to make sure the matching monitor is muted or removed.
Code example
import { createClient } from '@sanity/client'
import { NextRequest, NextResponse } from 'next/server'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_API_TOKEN!,
useCdn: false,
})
const checklyHeaders = {
Authorization: `Bearer ${process.env.CHECKLY_API_KEY}`,
'X-Checkly-Account': process.env.CHECKLY_ACCOUNT_ID!,
'Content-Type': 'application/json',
}
export async function POST(req: NextRequest) {
const { _id } = await req.json()
const id = _id.replace(/^drafts\./, '')
const page = await sanity.fetch(`*[_id == $id][0]{
title,
"path": "/" + slug.current,
"expectedText": coalesce(checkly.expectedText, title),
"frequency": coalesce(checkly.frequency, 10),
"locations": coalesce(checkly.locations, ["us-east-1"])
}`, { id })
if (!page?.path) return NextResponse.json({ skipped: true })
const tag = `sanity:${id}`
const url = JSON.stringify(`${process.env.SITE_URL}${page.path}`)
const expectedText = JSON.stringify(page.expectedText)
const checks = await fetch('https://api.checklyhq.com/v1/checks', {
headers: checklyHeaders,
}).then((res) => res.json())
const existing = checks.find((check: any) => check.tags?.includes(tag))
const body = {
name: `Content smoke: ${page.title}`,
checkType: 'BROWSER',
activated: true,
frequency: page.frequency,
locations: page.locations,
tags: ['sanity', tag],
script: `const { test, expect } = require('@playwright/test')
test('page renders expected content', async ({ page }) => {
await page.goto(${url})
await expect(page.getByText(${expectedText})).toBeVisible()
})`,
}
const res = await fetch(`https://api.checklyhq.com/v1/checks${existing ? `/${existing.id}` : ''}`, {
method: existing ? 'PUT' : 'POST',
headers: checklyHeaders,
body: JSON.stringify(body),
})
if (!res.ok) throw new Error(await res.text())
return NextResponse.json({ ok: true, checkId: existing?.id })
}How Sanity + Checkly works
Build your Checkly integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Checkly monitoring to the content your users actually see.
Start building free →CMS approaches to Checkly
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Creating monitors from published content | Teams often copy URLs into Checkly after launch, which works for small sites but breaks down when campaigns, locales, and docs pages change weekly. | Webhooks or Functions can react to publish events, use GROQ to fetch monitoring fields, and call Checkly with a stable sanity:<id> tag. |
| Field-level check configuration | Monitoring details are usually stored outside the editing workflow, so editors can’t see which headline, selector, or route is being tested. | Schema fields in Sanity Studio can define expected text, selector, frequency, and Checkly locations, while GROQ returns the final payload in one query. |
| Unpublish and delete handling | Retired pages can leave active checks behind, causing false failures until someone cleans them up. | Mutation events include document IDs, so the integration can find checks tagged with sanity:<id> and mute or delete them in Checkly. |
| Content-aware alert context | Alerts usually mention a URL, but not the content owner, locale, campaign, or source document. | GROQ can include owner, market, campaign, and release fields in the Checkly check name, tags, or alert notes. |
| Monitoring web, mobile, and API routes | Page-focused content often maps well to website checks, but API and app routes need separate tracking. | One structured back end can define monitorable routes for websites, mobile deep links, API endpoints, and AI-agent-facing content. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Sentry
Connect content releases to runtime errors so teams can see when a publish correlates with frontend exceptions.
Sanity + Datadog
Send content deployment and publish events into Datadog to compare content changes with infrastructure and application metrics.
Sanity + LogRocket
Pair content-aware monitoring with session replay so teams can inspect what users saw after a content-driven route failed.