Sanity SEO content workflow with deterministic documents

The Sanity publisher creates or replaces one stable document for each article. It keeps the slug in Sanity's native slug shape, records SEODrafts provenance under a namespaced object, and lets the tenant map HTML, Markdown, metadata, or canonical fields to its own schema.

Best for: Sanity-backed marketing sites with a known document type and frontend query that can render the mapped article fields on a canonical route.

Support status: stable
Publisher: sanity

How the connection works

Authentication

A Sanity write token plus Project ID and dataset. The server-side publisher can also set the API version and document type so the mutation targets the intended schema.

Server-side variables

  • SEO_BLOG_PUBLISH_TOKEN
  • SEO_BLOG_SITE_SLUG
  • SANITY_TOKEN
  • SANITY_PROJECT_ID
  • SANITY_DATASET
  • SANITY_DOCUMENT_TYPE
  • SEO_BLOG_SITE_ORIGIN

Publishing sequence

01

Derive a stable document ID from the article slug.

02

Create fields from the default mapping and tenant overrides.

03

Write the native slug object and namespaced provenance metadata.

04

Submit a create-or-replace mutation to the configured dataset and retain the frontend URL.

No-write payload and mapping preview

SEO_BLOG_PUBLISH_TOKEN='<tenant-token>' \
SEO_BLOG_SITE_SLUG='<slug>' \
SANITY_TOKEN='<sanity-token>' \
SANITY_PROJECT_ID='<sanity-project-id>' \
SANITY_DATASET='<sanity-dataset>' \
SANITY_DOCUMENT_TYPE='<sanity-document-type>' \
SEO_BLOG_SITE_ORIGIN='<origin>' \
npx --yes @wotaso/seo-blog-admin-cli publish-cms --cms sanity --site-slug '<slug>' --site-origin '<origin>' --dry-run

For a currently due approved post, --dry-run builds the destination payload without writing to the provider. It does not contact the provider or prove credentials, remote schema compatibility, deployment, or a live page; when no post is due, provider inputs are not exercised.

Scheduled runner health heartbeat

SEO_BLOG_PUBLISH_TOKEN='<tenant-token>' \
SEO_BLOG_SITE_SLUG='<slug>' \
SANITY_TOKEN='<sanity-token>' \
SANITY_PROJECT_ID='<sanity-project-id>' \
SANITY_DATASET='<sanity-dataset>' \
SANITY_DOCUMENT_TYPE='<sanity-document-type>' \
SEO_BLOG_SITE_ORIGIN='<origin>' \
SEO_BLOG_SCHEDULED='true' \
npx --yes @wotaso/seo-blog-admin-cli health --config seo-blog.config.json --mode cms --site-slug '<slug>' --site-origin '<origin>'

Run this from the real scheduled runner after seo-blog.config.json contains the selected connector configuration and the listed secrets are loaded. It reports configuration readiness, not a live article. The dashboard expires readiness at the returned deadline, with a 36-hour fallback when no deadline is available.

Fields and data model

  • Both bodyHtml and bodyMarkdown are available to the schema
  • Slug is written as a native Sanity slug object
  • Document type, API version, and field map are configurable

Canonical and route

Sanity stores content, but the consuming frontend controls the page route, canonical tag, sitemap, structured data, and deployment. SEODrafts derives the final route from the configured website rather than a Studio URL.

Known limits

  • Portable Text blocks are not invented from HTML without a tenant mapping or transformation.
  • Create-or-replace requires a token that can write the chosen dataset.
  • A successful mutation does not confirm that a cached or statically built frontend is live.

Recovery

  • Test the exact document type and field map against a non-production dataset first.
  • Inspect schema validation errors in Sanity when a field type rejects the mapped value.
  • Reuse the deterministic document ID after fixing a failure instead of creating another document.

Integration questions

Where are credentials stored?

The Sanity credentials stay in CI or another server-side secret environment.

Does SEODrafts own the canonical URL?

No. The destination website owns its route, canonical tag, sitemap, rendering, and analytics.

Can the integration be tested before publishing?

Use the no-write payload preview to inspect local mapping, then separately test credentials and the first destination draft before a live run.

Keep the website your team already owns.

Review the opportunity and draft in SEODrafts, then publish approved work through Sanity.

Add my website