Setting up Sanity webhook API to trigger Github actions dispatch workflow

6 replies
Last updated: Mar 14, 2022
Hey there, I’m trying to setup Sanity webhook API to trigger Github actions dispatch workflow. However I’m running into below issue. I already had a test via Curl before setting up sanity webhook. Can anyone suggest how this can be achieved?
{
id: "atm-xxxxx",
projectId: "xxx",
inProgress: false,
duration: 226,
createdAt: "2022-02-xxxxx",
updatedAt: "2022-02-xxxx",
messageId: "msg-xxxxxxxxxx",
hookId: "xxxxxxxxx",
isFailure: true,
failureReason: "other",
resultCode: 422,
resultBody: "{"message":"Invalid request.\n\n\"_createdAt\", \"_id\", \"_rev\", \"_type\", \"_updatedAt\", \"header\", \"slug\", \"title\" are not permitted keys.\n\"event_type\" wasn't supplied.","documentation_url":"<https://docs.github.com/rest/reference/repos#create-a-repository-dispatch-event>"}"
}
AI Update

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:

  1. Go to your Sanity project's webhook settings (Manage → API → Webhooks)
  2. 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 with repo scope)
      • Accept: application/vnd.github.v3+json
    • Projection: This is the key part - use something like:
{
  "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 }}"

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!

Thank you for your patience while we were out for a long holiday weekend.
I haven’t personally had enough experience with webhooks and github actions to give you a quick answer off the top of my head. But just at a glance, it seems that github isn’t happy with the object headers you are trying to send. I have asked the team for some input or similar working examples to get started.
Hi Dev C. It looks like your webhook’s projection is either blank (so everything is being sent) or not configured to what GitHub is expecting. From the URL at the bottom of the error, it looks like GitHub requires the
event_type
parameter. It also looks like if you want to pass details about the document, you’ll do so via the
client_payload
object parameter.
In your webhook setup, you’ll likely want your projection to look like this:


{
  'event_type': 'event_type'
}
or this to send specific fields:


{
  'event_type': 'event_type',
  'client_payload': {
    title,
    _id,
  }
}
or this to send full document details in the payload:


{
  'event_type': 'event_type',
  'client_payload': {
    ...
  }
}
In case you would like to test and replicate this in vision for groq webhooks in your project manager at sanityio, here is an quick query example of altering object ids that might help:
user A
How can we specify the event_type and client_payload parameters in Webhooks in your Sanity Content Lake? Another way is it possible? I can’t see where I can forge a special body parameters? Any advice?
EDIT: Maybe add a proxy endpoint for forging this Github usecase?
Now I understand this message from
user U
I figured out CROQ projection 😅
user U
Ha! I figured out CROQ projection 😅

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.

Was this answer helpful?