Creating draft edits programmatically with the Sanity client for JavaScript
You're running into the fundamental difference between how patch and create operations work. Here's what's happening and the correct approach:
The Core Issue
The patch operation in Sanity only works on existing documents - it cannot create new documents. When you want to create a draft programmatically, you need to create a new document with the drafts. prefix in the _id. Since this is a creation operation (not modifying an existing document), you must use a create-type mutation like createOrReplace.
From the Mutation API documentation, the available create operations are:
create- Creates a new document, fails if_idalready existscreateIfNotExists- Creates only if the_iddoesn't existcreateOrReplace- Creates or replaces a document with that_id
The Solution
Use createOrReplace with the draft _id:
client.createOrReplace({
_id: `drafts.${documentId}`,
_type: 'yourDocumentType',
// ... your document fields
})Why this triggers a "create" webhook event: In Sanity's architecture, a draft is a separate document from its published counterpart - it just has drafts. prepended to the _id. When you use createOrReplace with drafts.post-123, you're creating (or replacing) that specific draft document, which the webhook system correctly identifies as a create event for that document ID.
Better Alternative: Sanity Functions
Since you're using webhooks to react to content changes, I'd strongly recommend switching to Sanity Functions instead. Functions are the modern, recommended approach for automation and event handling in Sanity, with several advantages:
- Native integration - Direct access to document events without external hosting
- Better event filtering - Distinguish between draft and published changes more easily
- No infrastructure - Serverless compute runs on Sanity's infrastructure
- Automatic scaling - Handles traffic spikes automatically
- More precise control - Configure triggers for specific document events (publish, create, update, delete)
Functions can handle the same use cases as webhooks but with tighter integration and less complexity around event types.
Workaround for Webhooks
If you need to stick with webhooks, you can distinguish between "true creates" and "draft updates" by:
Checking for the published version - When your webhook receives a
createevent for a draft ID, query whether the published version exists:const publishedExists = await client.fetch( `defined(*[_id == $publishedId][0])`, { publishedId: documentId.replace('drafts.', '') } ) // If publishedExists is true, treat as updateUsing GROQ filters in webhooks - The webhook filtering system allows you to create separate webhooks for drafts vs published documents using path filters like
_id in path("drafts.*")to separate draft events from published events.
The key takeaway: patch operations fundamentally cannot create documents - they only modify existing ones. Creating a draft requires a create-type operation, which will always trigger a create event in webhooks.
Sanity β Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.