Setting up Sanity webhook API to trigger Github actions dispatch workflow
I can see the issue! GitHub's repository dispatch API requires a specific payload structure, but Sanity webhooks by default send the entire document with all its Sanity-specific fields like _id, _type, _createdAt, etc. GitHub is rejecting this because it only expects event_type (required) and optionally client_payload.
Here's how to fix this:
Configure Your Sanity Webhook with a Custom Projection
When setting up your webhook in Sanity (either via the Studio or API), you need to use a projection to transform the payload into the format GitHub expects:
- Go to your Sanity project's webhook settings (Manage → API → Webhooks)
- Create or edit your webhook with these settings:
- URL:
https://api.github.com/repos/{owner}/{repo}/dispatches - Dataset: Choose your dataset
- HTTP method: POST
- HTTP Headers:
Authorization: Bearer YOUR_GITHUB_PAT(Personal Access Token withreposcope)Accept: application/vnd.github.v3+json
- Projection: This is the key part - use something like:
- URL:
{
"event_type": "sanity-webhook",
"client_payload": {
"documentId": _id,
"documentType": _type,
"slug": slug.current,
"title": title
}
}The projection uses GROQ syntax to reshape your document data into the exact structure GitHub expects. The event_type field is required by GitHub, and client_payload can contain whatever custom data you want to pass to your GitHub Actions workflow.
In Your GitHub Actions Workflow
You can then access this data in your workflow:
on:
repository_dispatch:
types: [sanity-webhook]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Use payload data
run: |
echo "Document ID: ${{ github.event.client_payload.documentId }}"
echo "Title: ${{ github.event.client_payload.title }}"Alternative: Use Sanity Functions (Recommended)
If you need more control over the payload transformation or want to add conditional logic, consider using Sanity Functions instead of direct webhooks. Functions are the modern, recommended approach for reacting to content changes - they're serverless compute modules that run within Sanity with native integration, automatic scaling, and better security.
Functions can listen to document changes and make custom HTTP requests with complete control over the payload format:
// sanity.blueprint.ts
import {defineBlueprint} from 'sanity'
export default defineBlueprint({
functions: [
{
name: 'trigger-github-action',
runtime: 'nodejs22',
handler: './functions/triggerGithub',
events: {
documentChanges: {
filter: '_type == "post"'
}
}
}
]
})Then in your function handler, you have full control to format the GitHub API request exactly as needed, handle errors, add retry logic, etc.
The webhook projection approach should solve your immediate issue though - just make sure your GitHub PAT has the correct repo scope and the projection transforms your document into the event_type + client_payload structure GitHub expects!
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.