Sanity integration

Generate PDFs from Sanity

Connect Quaterio to your Sanity project, write a GROQ query and bind the result to a block-based template. Public datasets need no auth; private ones use a Viewer token. The connector also speaks the CDN host when you need edge-cached speed.

The problem

Sanity gives content teams a real-time studio and a flexible content model. PDF output is not part of the deal. Custom rendering pipelines built on @portabletext/to-html plus Puppeteer get the job done once, then need ongoing maintenance every time the schema changes.

How Quaterio handles it

Quaterio's Sanity connector hits the GROQ HTTP query API at PDF generation time. You write a GROQ query (or use the preset starter), the connector fetches and Quaterio drops the result into your template blocks. No custom Express endpoints, no separate renderer.

How it works

  1. 1

    Pick the Sanity preset in Connections

    Settings → Connections → New Connection → pick "Sanity" from the type dropdown. Replace abc123 with your project ID and production with your dataset name. Adjust the GROQ query to whatever you want to render (URL-encoded).

  2. 2

    Paste a token if the dataset is private

    If your dataset is private, generate a Viewer-rights API token under sanity.io/manage → API → Tokens and paste it into the Token field. Public datasets leave the field blank.

  3. 3

    Hit Detect Fields to confirm the shape

    Quaterio runs your GROQ query and shows the JSON paths in the actual response. The default Response Field result.0.body assumes a posts query. For other shapes pick the right path with one click.

  4. 4

    Generate the PDF via the editor or API

    Export from the editor for one-offs. For batch jobs hit POST /api/v1/generate/template with the template ID. The connector re-fetches at generation time so every PDF reflects the latest Sanity content.

What the connector handles

  • GROQ HTTP query API: any dataset, any query, any projection
  • Public datasets need no token. Private ones use Viewer rights minimum
  • CDN host (apicdn.sanity.io) supported for edge-cached reads
  • Date-pinned API version so old queries keep working after upstream releases
  • POST mode for GROQ queries longer than the 11 KB URL limit
  • Image and file references resolved via the GROQ arrow operator (asset->url)
  • Detect Fields walks the actual response after your query runs
  • Honest about Portable Text: returned as JSON blocks, not HTML, use Markdown fields for prose
  • 15 second fetch timeout, cached on the block when the API is slow or unreachable
  • Refresh on demand from the editor, or via API before batch generation

Generate via API

# 1. Create the connection via the dashboard (one-time setup). # 2. Bind a template's Remote Content block to it. # 3. Generate a PDF that pulls live Sanity content: curl -X POST https://quaterio.com/api/v1/generate/template \ -H "Authorization: Bearer q_your_token" \ -H "Content-Type: application/json" \ -d '{ "templateId": "sanity_blog_export" }' # → { # "ok": true, # "data": { "downloadUrl": ".../blog_en_a7f3b2c1.pdf" } # } # The connector runs your GROQ query at generation time and # drops the result into the bound template block.

Available on Business plan and above.

Sanity and PDF: the real questions

The Sanity preset uses the GROQ HTTP query API. You pick a project, a dataset and a GROQ query; Quaterio fetches the result at PDF generation time. Public datasets need no auth. Private datasets need a read-only API token. The sections below cover the URL shape, the GROQ encoding gotcha, Portable Text and CDN caching.

Project ID, dataset and API version

Three pieces go into the URL. Project ID is the short ID in your Sanity Studio URL (sanity.io/manage shows it). Dataset is usually "production" or "development". The api version is a date string like v2024-01-01, Sanity uses date-based versioning so old queries keep working when they release breaking changes.

Use the date of the day you set up the connection, not today. Once a query works against v2024-01-01 it continues to work even after Sanity ships v2026-06-09. Pinning the version protects your PDFs from upstream changes.

GROQ in the URL must be URL-encoded

GROQ queries contain characters that are not URL-safe: brackets, quotes, double-equals, asterisks. The preset URL ships with an encoded example (*%5B_type%3D%3D%22post%22%5D which decodes to *[_type=="post"]). When you change the query, encode it: spaces become %20, brackets %5B / %5D, quotes %22.

The HTTP GET method has an 11 KB query size limit at Sanity. If your GROQ query is longer than that, switch the connection method to POST and put the query in the request body as JSON. The connector form supports both.

Public vs private datasets, when do you need a token

A public dataset (the default for production in most setups) needs no auth. Quaterio leaves the Token field blank and the API responds normally. For private datasets, generate an API token under sanity.io/manage → Your project → API → Tokens with at least Viewer rights, and paste it into Quaterio.

The token sits in the Authorization Bearer header, not on the URL. Quaterio adds it automatically when you populate the field. Rotate the token in Sanity if it leaks; the connector handles the new value as soon as you update the connection.

Portable Text is JSON blocks, not HTML

Sanity's rich text format is called Portable Text. It is returned as an array of typed blocks ([{ _type: "block", children: [{ _type: "span", text: "...", marks: [...] }] }]), not as rendered HTML. Binding a Portable Text field straight to a Remote Content block puts the JSON into your PDF.

Two workable paths today. First, model your long-form copy as a plain text or Markdown field instead of Portable Text. Quaterio renders Markdown into HTML at generation time. Second, render the Portable Text to HTML in a small serverless function and store the result as a separate string field. Native Portable Text rendering is on the roadmap but not first-party today.

CDN vs API host (the speed dial)

Sanity exposes two hosts. {projectId}.api.sanity.io is the live API, every request hits Sanity directly. {projectId}.apicdn.sanity.io is the edge-cached CDN, responses are cached at the network edge for ~10 seconds. The CDN is significantly faster and free of request fees on most plans; the live API is paid per request after a quota.

For PDF generation use the CDN host unless you need real-time content. The preset URL ships with .api.sanity.io for safety; swap it to .apicdn.sanity.io if your dataset is public and a 10 second freshness window is fine for your PDFs. Token authentication still works on the CDN host.

Response shape and the "result is not always an array" trap

GROQ is more like a programming expression than SQL. *[_type=="post"] returns an array. *[_type=="post"][0] returns one document. *[_type=="post"][0].title returns a string. The Sanity response wraps whatever you asked for in { result, query, ms, syncTags }.

If your Response Field expects result.0.body but your query returns a single object directly (no array), the path is just result.body. Use Detect Fields after writing your query, Quaterio walks the actual response and shows you the real paths, regardless of what the GROQ docs implied.

Image and file references

Sanity stores images and files as references with their own _ref ID, not inline. To get an image URL into a PDF, project it in your GROQ query: ...{ "hero": hero.asset->url }. The arrow operator dereferences the asset and pulls the canonical URL onto the result object.

For image transformations (resize, crop, hotspot), append Sanity's image URL params: ...{ "hero": hero.asset->url + "?w=1200&fit=max" }. Quaterio binds the resulting URL into the image block at render time.

Pricing and plan tier

CMS connections are part of the Business plan and above ($30/mo). Pro plans get the editor and basic templates but not the Remote Content block. Free and Pro users who try to add a Remote Content block see an upgrade prompt.

Sanity's free plan covers up to 3 users, 2 datasets and 500K API CDN requests per month. That is plenty for evaluation and small production PDF workloads. Paid Sanity plans start at $99/mo for additional capacity and SSO. There is no extra per-API-call cost from Quaterio.

Try the Sanity connector free

Set up the connection in two minutes. No credit card required to start.