How to Integrate Lemon Squeezy with Your Headless CMS
Sync product pages, pricing copy, and checkout links between Lemon Squeezy and your headless CMS so customers always see the right plan before they buy.
What is Lemon Squeezy?
Lemon Squeezy is a merchant of record platform for selling software, digital products, subscriptions, and license keys. It handles checkout, payments, sales tax, VAT, subscriptions, invoices, and order webhooks, which makes it common for SaaS teams, indie software companies, and digital product sellers. Teams often use it when they want commerce operations handled without building billing, tax, and payment flows from scratch.
Why integrate Lemon Squeezy with a headless CMS?
If your product page lives in one system and your checkout configuration lives in Lemon Squeezy, small changes can turn into risky manual work. A pricing page update might require an editor to change plan copy, a developer to update frontend constants, and an operations teammate to confirm the matching Lemon Squeezy variant ID. That works for 3 products. It gets messy when you have 20 plans, 4 currencies, limited-time discounts, comparison tables, and launch pages going live at 9:00 AM.
Architecture overview
A typical flow starts when an editor publishes a product or plan document in Sanity Studio. That document is written to the Content Lake with fields such as title, slug, shortDescription, priceDisplay, feature references, lemonSqueezyProductId, and lemonSqueezyVariantId. A Sanity webhook fires on the publish event, or a Sanity Function runs on the content mutation, and the handler receives the changed document ID. The handler then runs a GROQ query to fetch only the fields Lemon Squeezy needs, including joined feature copy from referenced documents. From there, server-side code calls Lemon Squeezy's API or the @lemonsqueezy/lemonsqueezy.js SDK with a private API key. For example, it can update a variant's public name and description, create a checkout for a specific variant, or refresh checkout URLs used by the frontend. The customer-facing site queries Sanity for the product page, renders the current copy and comparison table, and sends the buyer to Lemon Squeezy checkout for payment, tax, subscriptions, license keys, and receipts. Lemon Squeezy order and subscription webhooks can then update your app account system after purchase.
Common use cases
SaaS pricing pages
Model Free, Pro, and Team plans in Sanity, map each plan to a Lemon Squeezy variant, and keep plan copy, feature rows, and checkout buttons in sync.
License key product launches
Publish a launch page from Sanity and route buyers to Lemon Squeezy products that issue license keys after payment.
Digital downloads
Use Sanity for product storytelling, screenshots, changelogs, and FAQs while Lemon Squeezy handles file delivery, receipts, and tax.
Campaign-specific checkouts
Create seasonal landing pages in Sanity that point to Lemon Squeezy variants, discounts, or prefilled checkout URLs for specific campaigns.
Step-by-step integration
- 1
Set up Lemon Squeezy
Create a Lemon Squeezy account, add your store, create products and variants in the dashboard, and copy the store ID, product IDs, and variant IDs. Generate an API key from Lemon Squeezy settings, and keep it server-side only.
- 2
Install the SDKs
In your app or Sanity Function project, install @lemonsqueezy/lemonsqueezy.js for Lemon Squeezy API calls and @sanity/client for GROQ reads from the Content Lake.
- 3
Model products in Sanity Studio
Create a product or plan schema with fields like title, slug, shortDescription, priceDisplay, feature references, lemonSqueezyProductId, lemonSqueezyVariantId, and checkoutCta. Keep billing rules in Lemon Squeezy, not in editable content fields.
- 4
Create the sync trigger
Add a Sanity webhook that fires when product documents are published, or use a Sanity Function if you want the sync code to run on Sanity's event system without running a separate server. Configure the webhook payload to include _id and _type.
- 5
Call Lemon Squeezy from server-side code
Use GROQ to fetch the changed product, then call Lemon Squeezy's API with the mapped product or variant ID. Common actions include updating variant display fields, creating checkout links, or validating that every published plan has a matching Lemon Squeezy variant.
- 6
Test the storefront and checkout path
Publish a test product, confirm the sync response, load the frontend page, click through to Lemon Squeezy checkout, and verify that order or subscription webhooks update your app after purchase.
Code example
import { createClient } from "@sanity/client";
import { lemonSqueezySetup, updateVariant } from "@lemonsqueezy/lemonsqueezy.js";
lemonSqueezySetup({ apiKey: process.env.LEMONSQUEEZY_API_KEY! });
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: "2025-02-19",
useCdn: false,
token: process.env.SANITY_READ_TOKEN
});
const query = `*[_id == $id][0]{
title,
"description": shortDescription,
"variantId": lemonSqueezy.variantId,
priceCents
}`;
export async function POST(req: Request) {
const event = await req.json();
const product = await sanity.fetch(query, { id: event._id });
if (!product?.variantId) {
return Response.json({ skipped: true });
}
const { data, error } = await updateVariant(product.variantId, {
name: product.title,
description: product.description,
price: product.priceCents
});
if (error) {
return Response.json({ error: error.message }, { status: 502 });
}
return Response.json({ syncedVariantId: data?.id });
}How Sanity + Lemon Squeezy works
Build your Lemon Squeezy integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect product content with Lemon Squeezy checkout flows.
Start building free →CMS approaches to Lemon Squeezy
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Product content modeling | Product copy often sits inside pages, so plan names, feature rows, and checkout IDs can be hard to reuse safely. | Schema-as-code lets you model plans, feature groups, FAQs, launch windows, and Lemon Squeezy IDs as versioned fields. |
| Sync on publish | Teams often rely on manual updates or scheduled scripts after a pricing page changes. | GROQ-powered webhooks and Functions can trigger only for product documents and send the exact fields your sync needs. |
| Checkout field ownership | Payment-related values can get mixed into page content, which raises the chance of editing mistakes. | Sanity Studio can show editable marketing fields while keeping Lemon Squeezy product, variant, and billing identifiers controlled. |
| Multi-channel product pages | Content is usually built for the website first, then copied into landing pages, apps, or emails. | GROQ can join product copy, feature references, media, and checkout IDs for web, mobile, email, and agent experiences. |
| Operational trade-offs | Simple for small sites, but manual commerce updates become risky as product count grows. | Functions can run sync logic on content events, though you'll still need clear rules for retries, id mapping, and which fields Lemon Squeezy owns. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Shopify
Use Shopify for physical product commerce while Sanity structures product storytelling, buying guides, and multi-channel content.
Sanity + Stripe
Build custom billing or checkout flows with Stripe while Sanity controls pricing pages, plan comparisons, and launch content.
Sanity + Medusa
Connect Sanity with Medusa when you need a custom commerce engine, structured product content, and flexible storefront delivery.