Unlock seamless workflows and faster delivery with our latest releases – get the details

Webhooks API

API endpoints for managing Sanity webhooks

Types

Sanity provides two types of webhooks, transaction and document. Currently document webhooks are preferred because they are more flexible and powerful.

Document

A document webhook triggers every time a document is created, updated, or deleted. If a transaction updates 3 documents, 3 webhooks will be executed. Document webhook also allows for more granular filtering and customisable payloads with GROQ.

For more information about document webhooks click here.

Transaction

A transaction webhook triggers once per dataset , meaning if you batch together multiple document mutations in one transaction only one webhook will be executed.

Endpoints

List all webhooks

GET /v2021-10-04/hooks/projects/${projectId}

Request

curl 'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}' \
--header 'Authorization: Bearer ${token}'

Response

[
    {
      "type": "document",
      "rule": {
        "on": [
          "create",
          "update"
        ],
        "filter": "_type == \"article\"",
        "projection": "{ _id, name }"
      },
      "apiVersion": "v2021-03-25",
      "httpMethod": "POST",
      "includeDrafts": false,
      "headers": {},
      "secret": "thisIsAsecret",
      "id": "nCA0wzdJXR1cVadR",
      "name": "test",
      "projectId": "exx11uqh",
      "dataset": "*",
      "url": "https://www.example.com/",
      "createdAt": "2022-07-22T10:48:21.392Z",
      "createdByUserId": "psh9hAsjB",
      "isDisabled": false,
      "isDisabledByUser": false,
      "deletedAt": null,
      "description": null
    },
    {
      "type": "transaction",
      "id": "yyB57FRgCOCeLAtL",
      "name": "Transactio webhook",
      "projectId": "exx11uqh",
      "dataset": "webhook-test",
      "url": "https://www.example.com/",
      "createdAt": "2022-07-22T10:48:21.392Z",
      "createdByUserId": "pdakbqZhE",
      "isDisabled": false,
      "isDisabledByUser": false,
      "deletedAt": null,
      "description": null
    },
    ...
  ]

Get a webhook by ID

GET /v2021-10-04/hooks/projects/${projectId}/${hookId}

Request

curl 'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}/${hookId}' \
--header 'Authorization: Bearer ${token}'

Response

{
  "type": "document",
  "rule": {
    "on": [
      "create",
      "update"
    ],
    "filter": "_type == \"article\"",
    "projection": "{ _id, name }"
  },
  "apiVersion": "v2021-03-25",
  "httpMethod": "POST",
  "includeDrafts": false,
  "headers": {},
  "secret": "thisIsAsecret",
  "id": "nCA0wzdJXR1cVadR",
  "name": "test",
  "projectId": "exx11uqh",
  "dataset": "*",
  "url": "https://www.example.com/",
  "createdAt": "2022-07-22T10:48:21.392Z",
  "createdByUserId": "psh9hAsjB",
  "isDisabled": false,
  "isDisabledByUser": false,
  "deletedAt": null,
  "description": null
}

Create a document webhook

POST /v2021-10-04/hooks/projects/{projectId}

Request

curl \
  'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}' \
  --request POST \
  --header 'Authorization: Bearer ${token}' \
  --json @- << EOF
{
  "type":"document",
  "name":"name",
  "url":"https://www.example.com/",
  "httpMethod":"POST",
  "apiVersion":"v2021-03-25",
  "includeDrafts":false,
  "dataset":"*",
  "rule":{
    "on":["create"],
    "filter":"_type == 'article'",  
    "projection":"{_id}"      
  },
  "description":"description",
  "headers":{},
  "secret":"MySecret",
  "isDisabledByUser":false
}
EOF

Response

{
  "type": "document",
  "rule": {
    "on": [
      "create"
    ],
    "filter": "_type == 'article'",
    "projection": "{_id}"
  },
  "filter": null,
  "projection": null,
  "apiVersion": "v2021-03-25",
  "httpMethod": "POST",
  "includeDrafts": false,
  "headers": {},
  "secret": "MySecret",
  "id": "RHlOxIEkZd8rncl6",
  "name": "name",
  "projectId": "exx11uqh",
  "dataset": "*",
  "url": "https://www.example.com/",
  "createdAt": "2023-07-21T09:13:30.754Z",
  "createdByUserId": "psv96AOjB",
  "isDisabled": false,
  "isDisabledByUser": false,
  "deletedAt": null,
  "description": "description"
}

Request parameters

  • REQUIREDtypestring

    'document'

  • REQUIREDnamestring

    Name of webhook

  • REQUIREDurlstring

    The URL field is where you specify the endpoint to which the webhook request is sent.

  • REQUIREDdatasetstring

    Which dataset should this trigger for.

    '*' means all datasets in the project.

  • descriptionstring

    Optional description to add helpful context to the webhook.

  • rule.onstring[]

    Webhooks can be triggered when a document is created, updated, deleted, or any combination of these.

    • 'create': triggers on the creation of a new document.
    • 'update': triggers on every change to a document once created.
    • 'delete': triggers on the deletion of a document
  • rule.filterstring

    A GROQ filter specifying which documents will, when changed, trigger your webhook. A filter is what you commonly see between the *[ and ] in a GROQ query. This field supports all the GROQ functions you'd expect and has additional support for functions in the delta:: namespace, as well as before() and after().

    If left empty, it will apply to all documents (*[])

  • rule.projectionstring

    A GROQ projection defining the payload (or body) of the outgoing webhook request. This field supports GROQ functions in the delta:: namespace, as well as before() and after().

    If left empty, it will include the whole document after the change that triggered it.

    See more information about GROQ-projections here

  • apiVersionstring

    Which API version to use for the GROQ filter and projection. Defaults to the latest API version for the GROQ query API

  • httpMethodstring

    This field configures the webhook's HTTP request method. It can be set to POST, PUT, PATCH, DELETE, or GET. Some endpoints require incoming requests to use a specific method to work.

  • includeDraftsboolean

    Whether or not to trigger on draft changes

  • headersRecord<string, string>

    Additional HTTP headers. You can add multiple headers. A common example is adding an Authorization: Bearer <token> header to authenticate the webhook request.

  • secretstring

    To let receiving services verify the origin of any outgoing webhook, you may add a secret that will be hashed and included as part of the webhook request's headers. You may find our webhook toolkit library helpful for working with secrets.

  • isDisabledByUserboolean

    Set to 'true' to disable the webhook and prevent it from triggering.

Create a transaction webhook

POST /v2021-10-04/hooks/projects/{projectId}

Request

curl \
  'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}' \
  --request POST \
  --header 'Authorization: Bearer ${token}' \
  --json @- << EOF
{
  "type":"transaction",
  "name":"name-transaction",
  "url":"https://www.example.com/",
  "dataset":"*",
  "description":"description",
  "isDisabledByUser":false
}
EOF

Response

{
  "type": "transaction",
  "id": "8ngGSobiUAzpw4cl",
  "name": "name-transaction",
  "projectId": "exx11uqh",
  "dataset": "*",
  "url": "https://www.example.com/",
  "createdAt": "2023-07-21T10:32:48.424Z",
  "createdByUserId": "psv96AOjB",
  "isDisabled": false,
  "isDisabledByUser": false,
  "deletedAt": null,
  "description": "description"
}

Request parameters

  • REQUIREDtypestring

    'transaction'

  • REQUIREDdatasetstring

    Which dataset should this trigger for.

    '*' means all datasets in the project.

  • REQUIREDurlstring

    The URL field is where you specify the endpoint to which the webhook request is sent.

  • REQUIREDnamestring

    Name of webhook

  • descriptionstring

    Optional description to add helpful context to the webhook

  • isDisabledByUserboolean

    Set to 'true' to disable the webhook and prevent it from triggering.

Update a webhook

PATCH /v2021-10-04/hooks/projects/{projectId}/{id}

Request

curl \
  'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}/${id}' \
  --request PATCH \
  --header 'Authorization: Bearer ${token}' \
  --json @- << EOF
{
  "isDisabledByUser":true
}
EOF

Response

{
  "type": "transaction",
  "id": "8ngGSobiUAzpw4cl",
  "name": "name-transaction",
  "projectId": "exx11uqh",
  "dataset": "*",
  "url": "https://www.example.com/",
  "createdAt": "2023-07-21T10:32:48.424Z",
  "createdByUserId": "psv96AOjB",
  "isDisabled": false,
  "isDisabledByUser": true,
  "deletedAt": null,
  "description": "description"
}

Delete a webhook

DELETE /v2021-10-04/hooks/projects/{projectId}/{id}

Request

curl \
  'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}/${id}' \
  --request DELETE \
  --header 'Authorization: Bearer ${token}'

Response

{
  "id": "8ngGSobiUAzpw4cl",
  "deleted": 1
}

List webhook attempts

GET /v2021-10-04/hooks/projects/${projectId}/{id}/attempts

Request

curl \
  'https://api.sanity.io/v2021-10-04/hooks/projects/${projectId}/${id}/attempts' \
  --header 'Authorization: Bearer ${token}'

Response

[
  {
    "id": "ROnIqO5K2v8fvJGz-YLiSJVES65n2uvB1osaQEMEXUqmoUmPn",
    "projectId": "exx11uqh",
    "inProgress": false,
    "resultBody": "OK",
    "resultCode": 200,
    "duration": 39,
    "isFailure": false,
    "failureReason": null,
    "createdAt": "2023-07-21T10:46:40.147Z",
    "updatedAt": "2023-07-21T10:46:40.239Z",
    "deletedAt": null,
    "messageId": "ROnIqO5K2v8fvJGz-aa1cmvIZmBc1lFSopuuGrJbNNyfq6VVM",
    "hookId": "ROnIqO5K2v8fvJGz"
  },
  {
    "id": "ROnIqO5K2v8fvJGz-54BtIsybhwP9Q4FDFNVnjeYbKe6r6mV3",
    "projectId": "exx11uqh",
    "inProgress": false,
    "resultBody": "Unauthorized",
    "resultCode": 401,
    "duration": 42,
    "isFailure": true,
    "failureReason": "http",
    "createdAt": "2023-07-21T10:45:40.226Z",
    "updatedAt": "2023-07-21T10:45:40.356Z",
    "deletedAt": null,
    "messageId": "ROnIqO5K2v8fvJGz-Ig96In7jBaYXRcx4qIDS6SYXP2fYeaoV",
    "hookId": "ROnIqO5K2v8fvJGz"
  },
  ...
]

Was this article helpful?