Publish content to any Sanity CMS instance. Use when asked to create a Sanity draft, push a document to Sanity, upload an image asset to Sanity, or convert c...
--- name: sanity-cms description: Publish content to any Sanity CMS instance. Use when asked to create a Sanity draft, push a document to Sanity, upload an image asset to Sanity, or convert content into a Sanity-formatted document. Works with any document type and schema — not limited to blog posts. Requires SANITY_PROJECT_ID and SANITY_API_TOKEN env vars. --- # Sanity CMS Skill Publishes documents to Sanity CMS via the Content API. Works with any schema. ## References - **API patterns (upload, mutate, query)**: `references/api.md` - **Portable Text body format**: `references/portable-text.md` ## Workflow ### 1. Understand the target schema Four ways to get schema info — try in this order: **A — File in workspace:** User drops schema at a known path (e.g. `sanity-schemas/blogPost.ts`). Read it directly. **B — Pasted in chat:** User pastes the schema TypeScript/JS. Read it from the conversation. **C — Remote URL:** User shares a GitHub raw URL or similar. Fetch with `web_fetch`. **D — API introspection (no file needed):** Query the dataset directly — see `references/api.md` (Schema Introspection section). Use `array::unique(*[]._type)` to discover document types, then fetch one sample document to infer field names and shapes. Works without any schema file at all. Once you have the schema (by any method): - For `array` fields with `type: 'block'`, use Portable Text format — see `references/portable-text.md` - For `reference` fields (categories, authors, tags), query existing documents via GROQ — see `references/api.md` ### 2. Format the document JSON Build a JSON object matching the schema: - Omit `_id` — the script generates a `drafts.` prefixed UUID automatically - Omit the cover image field — the script injects it after uploading - All required fields must be present and within any validation constraints - Save to a logical path (e.g. `brain/projects/<slug>-sanity.json` or similar) ### 3. Run the publish script The script is at `scripts/publish_draft.sh` within this skill directory. Resolve the path relative to where the skill is installed (e.g. `~/.openclaw/skills/sanity-cms/scripts/publish_draft.sh` or `<workspace>/skills/sanity-cms/scripts/publish_draft.sh`). ```bash bash <skill-dir>/scripts/publish_draft.sh \ path/to/document.json \ path/to/cover-image.png # optional ``` **Optional env overrides:** | Var | Default | Purpose | |-----|---------|---------| | `SANITY_DATASET` | `production` | Target dataset | | `COVER_IMAGE_FIELD` | `coverImage` | Field name for cover image | | `DRAFT_PREFIX` | `true` | Set to `false` to publish immediately | Example with overrides: ```bash SANITY_DATASET=staging COVER_IMAGE_FIELD=mainImage \ bash <skill-dir>/scripts/publish_draft.sh doc.json cover.jpg ``` ### 4. Confirm and report After the script prints a draft ID, report to the user: - The draft document ID - A link to Sanity Studio (ask if unsure of the Studio URL) - Which fields, if any, still need manual attention in Studio (e.g. unpopulated references) ## Env Vars | Var | Description | |-----|-------------| | `SANITY_PROJECT_ID` | Sanity project ID | | `SANITY_API_TOKEN` | Write-enabled token (Editor or higher) | | `SANITY_DATASET` | Dataset (optional, default: `production`) | ## Tips - Always create drafts first (`DRAFT_PREFIX=true`) unless the user explicitly wants to publish live - If a schema has `reference` fields, query for the referenced document IDs via GROQ before building the JSON — see `references/api.md` - The script works with any document type: blog posts, pages, products, authors, etc. - Cover image upload is optional — omit the second argument if the schema has no image field
don't have the plugin yet? install it then click "run inline in claude" again.
by @clawhub
extracted implicit decision logic into explicit if-else branches, added edge cases (auth expiry, network timeouts, empty schemas, malformed json), documented all external connections with env var setup, separated schema discovery into ordered decision tree, and clarified all-or-nothing transaction guarantees in output contract.
publish documents to any Sanity CMS instance via the Content API. use this when a user asks you to create a draft, push a document, upload an image asset, or convert content into Sanity format. works with any schema and document type (blog posts, pages, products, authors, etc.). the skill handles schema discovery, JSON formatting, image uploads, and draft creation in one workflow.
environment variables (required):
SANITY_PROJECT_ID: your Sanity project ID (string, no default)SANITY_API_TOKEN: write-enabled API token (Editor role minimum; no default)environment variables (optional):
SANITY_DATASET: target dataset name (default: production)COVER_IMAGE_FIELD: field name for cover/hero images (default: coverImage)DRAFT_PREFIX: create draft or publish live (default: true for drafts; set false to publish immediately)external connections:
SANITY_API_TOKEN)files and paths:
<skill-dir>/scripts/publish_draft.sh: the publish script (resolve path relative to skill installation)references/api.md: API patterns (upload, mutate, query, schema introspection, GROQ)references/portable-text.md: block field formatting guidestep 1: obtain the target schema (try in order)
1a. Check workspace for schema file at a known path (e.g. sanity-schemas/blogPost.ts). read it directly if present.
1b. Schema pasted in chat? Parse the TypeScript or JavaScript from the conversation.
1c. User shares a remote URL (GitHub raw, etc.). fetch it with web_fetch and parse the result.
1d. No schema file or URL available? Query the Sanity dataset directly using schema introspection (see references/api.md section "Schema Introspection"). use array::unique(*[]._type) to discover document types, then fetch one sample document to infer field names and shapes.
output from step 1: schema object with field definitions (types, validation rules, required fields).
step 2: understand field requirements from schema
2a. For any array field with type: 'block', the body must be Portable Text format (see references/portable-text.md).
2b. For any reference field (categories, authors, tags, etc.), query the target dataset via GROQ to get valid document IDs (see references/api.md section "GROQ queries"). do not invent reference IDs.
2c. Identify all required fields. note any validation constraints (min/max length, enum values, etc.).
output from step 2: field requirements checklist (which fields are required, which need Portable Text, which need reference lookups).
step 3: build the document JSON
3a. create a JSON object matching the schema. omit _id (the script generates a drafts. prefixed UUID). omit the cover image field (the script injects it after upload).
3b. populate all required fields. respect validation constraints (length, enum, etc.).
3c. save the JSON to a logical workspace path (e.g. brain/projects/<slug>-sanity.json).
output from step 3: valid document JSON file at a known path.
step 4: run the publish script
4a. resolve the script path relative to skill installation (e.g. ~/.openclaw/skills/sanity-cms/scripts/publish_draft.sh or <workspace>/skills/sanity-cms/scripts/publish_draft.sh).
4b. execute the script with the document JSON path and optional cover image path:
bash <skill-dir>/scripts/publish_draft.sh \
path/to/document.json \
path/to/cover-image.png
4c. if using optional env overrides (dataset, field names, draft vs. live):
SANITY_DATASET=staging COVER_IMAGE_FIELD=mainImage DRAFT_PREFIX=false \
bash <skill-dir>/scripts/publish_draft.sh doc.json cover.jpg
output from step 4: script prints the draft document ID and success/error messages to stdout.
step 5: report results to user
5a. extract the draft ID from script output.
5b. report back: the draft document ID, a clickable Sanity Studio link (ask user for Studio URL if unsure), and any fields that still need manual attention (unpopulated references, etc.).
output from step 5: user confirmation message with actionable next steps.
if user provides a schema file: read and parse it locally (step 1a). proceed to step 2.
else if user pastes schema in chat: extract and parse from conversation (step 1b). proceed to step 2.
else if user provides a remote schema URL: fetch with web_fetch, parse result (step 1c). proceed to step 2.
else if no schema is available: use schema introspection API query (step 1d). proceed to step 2.
if the schema has reference fields: query the Sanity dataset via GROQ to fetch valid document IDs before building the JSON (step 2b). do not proceed to step 3 until references are resolved.
if the schema has block (array) fields: format the body using Portable Text syntax (step 2a). do not use plain text.
if the user wants to publish live immediately: set DRAFT_PREFIX=false in step 4c. otherwise, always create a draft first (default behavior).
if the schema has no image field: omit the cover image argument in step 4b. the script still succeeds.
if the script returns an error (auth, network, validation): report the error to user. do not retry silently. common causes: expired SANITY_API_TOKEN, wrong SANITY_PROJECT_ID, network timeout, or malformed JSON. ask user to verify env vars or check network.
if the script returns a draft ID: proceed to step 5. if no draft ID in output, treat as failure and report error.
on success:
drafts. prefix in document ID)drafts.<uuid>)file outputs:
brain/projects/<slug>-sanity.json)error outputs:
user sees:
drafts.abc123def456)user can open Sanity Studio, navigate to the draft, and verify content looks correct.
credits: original skill by clawhub. enriched to Implexa standards.