Trigger Azure Pipeline build from Sanity Studio webhook
Triggering an Azure Pipeline build from Sanity Studio content changes is definitely achievable! Since you've got this working with Netlify, the Azure approach is similar conceptually but requires using the Azure DevOps REST API since Azure Pipelines doesn't have built-in webhook triggers like Netlify does.
The Correct Approach: Sanity Webhook β Azure DevOps REST API
Unlike Netlify's native webhook support, Azure Pipelines requires you to call its REST API to trigger builds. Here's the proper setup:
1. Create a Personal Access Token (PAT) in Azure DevOps
First, you need authentication credentials:
- Go to your Azure DevOps organization
- Click your profile icon β Personal access tokens
- Create a new token with Build (Read & Execute) permissions
- Save the token securely - you'll need it for the webhook
2. Set Up the Sanity Webhook
Go to your Sanity project settings β API β Webhooks, and create a new webhook with:
URL: Use the Azure DevOps REST API endpoint to trigger a pipeline run:
https://dev.azure.com/{organization}/{project}/_apis/pipelines/{pipelineId}/runs?api-version=7.1Replace:
{organization}- Your Azure DevOps organization name{project}- Your project name{pipelineId}- Your pipeline ID (find this in the pipeline URL or settings)
HTTP Method: POST
HTTP Headers: Add authentication:
Authorization: Basic {base64-encoded-pat}To create the base64-encoded PAT, encode :{your-pat} (colon followed by your token). Many online tools can do this, or use:
echo -n ":{your-pat}" | base64Trigger on: Select Create, Update, and/or Delete based on your needs
Filter: Use a GROQ filter to control which content changes trigger builds. For example:
_type == "post"Payload: You can customize the JSON payload using GROQ projections, but for Azure Pipelines, you typically just need to trigger the build. The default payload works fine, or use a minimal one:
{
"resources": {
"repositories": {
"self": {
"refName": "refs/heads/main"
}
}
}
}3. Alternative: Use an Intermediary (Recommended for Complex Scenarios)
If you need more control, security, or custom logic, consider using an Azure Function or other serverless function as middleware:
Sanity Webhook β Azure Function β Azure Pipeline
This approach gives you:
- Signature verification using the Sanity webhook toolkit
- Custom logic based on the content change
- Better secret management via Azure Key Vault
- Error handling and retry logic
Here's a basic Azure Function example:
module.exports = async function (context, req) {
const sanitySignature = req.headers['x-sanity-signature'];
// Verify webhook signature (recommended)
// Use @sanity/webhook package
// Trigger Azure Pipeline
const pipelineUrl = `https://dev.azure.com/${org}/${project}/_apis/pipelines/${pipelineId}/runs?api-version=7.1`;
const response = await fetch(pipelineUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${Buffer.from(':' + process.env.AZURE_PAT).toString('base64')}`
},
body: JSON.stringify({
resources: {
repositories: {
self: {
refName: 'refs/heads/main'
}
}
}
})
});
context.res = {
status: response.ok ? 200 : 500,
body: await response.text()
};
};4. Consider Sanity Functions (Modern Alternative)
If your use case allows, Sanity Functions provide a serverless compute environment within Sanity itself. You could use a Function to handle the webhook logic and call the Azure DevOps API directly, eliminating the need for external hosting:
// sanity.blueprint.ts
import {defineBlueprint} from 'sanity/blueprint'
export default defineBlueprint({
functions: [
{
name: 'trigger-azure-build',
on: {
documentEvent: {
filter: '_type == "post"',
events: ['create', 'update']
}
},
handler: async ({event}) => {
// Call Azure DevOps REST API
const response = await fetch(/* Azure pipeline URL */, {
method: 'POST',
headers: {/* auth headers */}
});
return {success: true};
}
}
]
});This requires a Growth plan or higher but provides native integration without managing external webhooks.
Debugging Tips
- Test the REST API directly using Postman or curl before setting up the webhook
- Check the Sanity webhook attempts log (three-dot menu in webhook settings) to see delivery status and responses
- Use webhook.site initially to verify Sanity is sending the webhook correctly
- Check Azure Pipeline run history to confirm builds are triggering
Helpful Headers from Sanity
Sanity automatically includes these headers with webhook requests that you can use for logging or conditional logic:
sanity-project-idsanity-datasetsanity-document-idsanity-operation(create/update/delete)x-sanity-signature(for verification)
For more details on configuring webhooks, check out the Sanity webhooks documentation and guides on GROQ-powered webhook filtering.
Show original thread2 replies
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.