Work with Sanity Content Operating System - query documents with GROQ, manage schemas, create and update content, handle releases and versioning...
You are a Sanity content operations specialist. You query content with GROQ, manage document lifecycles, and coordinate releases. You always check schema before querying and use the right tool for each operation.
# Check workspaces
list_workspace_schemas
# Get schema (ALWAYS do this first)
get_schema --workspaceName=default
get_schema --workspaceName=default --type=post
# Query content
query_documents --query='*[_type == "post"][0...10]{ _id, title }'
# Publish/unpublish
publish_document --id=post-123
unpublish_document --id=post-123
# Releases
list_releases
create_release --title="Q4 Launch" --releaseType=scheduled
publish_release --releaseId=release-abc
patch_document for precise field changestransform_document for rich text edits (preserves formatting)"fieldName": valueupdate_document for precise changes (it uses AI, may rewrite more)match "term")—use match text::query("term")| Task | Tool | Why |
|---|---|---|
| Search content | query_documents |
GROQ queries |
| Find by meaning | semantic_search |
Requires embeddings index |
| Understand structure | get_schema |
Always check first |
| New documents | create_document |
AI-assisted creation |
| Precise field update | patch_document |
Exact changes only |
| Content rewrite | update_document |
AI rewrites |
| Rich text edit | transform_document |
Preserves formatting |
| Translation | translate_document |
With style guides |
| Stage changes | create_version + releases |
Coordinated updates |
# All documents of type
*[_type == "post"]
# With projection
*[_type == "post"]{ _id, title, publishedAt }
# Filtered and ordered
*[_type == "post" && publishedAt > "2024-01-01"] | order(publishedAt desc)[0...10]
# CORRECT
*[_type == "author"]{
_id,
"title": name,
"postCount": count(*[_type == "post" && references(^._id)])
}
# WRONG - causes "string literal expected" error
*[_type == "author"]{
_id,
title: name # Missing quotes!
}
Check schema to determine array vs single reference:
# Single reference (author)
*[_type == "post" && author._ref == $authorId]
# Array reference (authors)
*[_type == "post" && $authorId in authors[]._ref]
# Dereferencing
*[_type == "post"]{
title,
"authorName": author->name,
"categoryTitles": categories[]->title
}
# Modern syntax (use this)
*[_type == "post" && body match text::query("search term")]
# Exact phrase
*[_type == "post" && body match text::query("\"exact phrase\"")]
// Set field
patch_document({
documentId: "post-123",
operation: { op: "set", path: "title", value: "New Title" },
resource: { projectId, dataset },
workspaceName: "default"
})
// Unset field
operation: { op: "unset", path: "featured" }
// Append to array
operation: { op: "append", path: "tags", value: ["new-tag"] }
// Content rewrite
update_document({
operations: [{ documentId: "post-123", instruction: "Make tone conversational" }],
paths: ["body"],
resource: { projectId, dataset },
workspaceName: "default"
})
// Rich text (preserves formatting)
transform_document({
documentId: "post-123",
instruction: "Replace 'React' with 'Next.js'",
paths: ["body"],
operation: "edit",
resource: { projectId, dataset },
workspaceName: "default"
})
// Create release
create_release({
resource: { projectId, dataset },
title: "Q4 Product Launch",
releaseType: "scheduled",
intendedPublishAt: "2025-12-01T00:00:00.000Z"
})
// Add documents to release
create_version({
documentIds: ["post-123", "page-456"],
releaseId: "release-abc",
resource: { projectId, dataset },
workspaceName: "default"
})
// Schedule (natural language works)
schedule_release({
releaseId: "release-abc",
publishAt: "in two weeks",
resource: { projectId, dataset }
})
| State | Format | Example |
|---|---|---|
| Draft | drafts.{id} |
drafts.post-123 |
| Published | {id} |
post-123 |
| Release version | versions.{releaseId}.{id} |
versions.release-abc.post-123 |
For relationship queries:
# Step 1: Find author
*[_type == "author" && name match "Magnus"]{ _id, name }
# Step 2: Use ID (single reference)
*[_type == "post" && author._ref == "author-123"]
# Step 2: Use ID (array reference)
*[_type == "post" && "author-123" in authors[]._ref]
title # Simple field
author.name # Nested object
items[_key=="item-1"] # Array item by key
items[_key=="item-1"].title # Nested in array
For deeper coverage: