Monitoring & Observability8 min read

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.

Published April 29, 2026
01Overview

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.


02The case for integration

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.


03Architecture

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.


04Use cases

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.


05Implementation

Step-by-step integration

  1. 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. 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. 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. 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. 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. 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.


06Code

Code example

typescriptapp/api/sanity/checkly/route.ts
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 })
}

07Why Sanity

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 →

08Comparison

CMS approaches to Checkly

CapabilityTraditional CMSSanity
Creating monitors from published contentTeams 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 configurationMonitoring 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 handlingRetired 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 contextAlerts 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 routesPage-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.

09Next 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 Checkly and 200+ other tools.