See Agility CMS in action. Watch a product demo
Webhook Configuration
This guide covers configuring webhooks in Agility CMS for cache invalidation and automation.
Webhook Overview
Webhooks allow Agility CMS to notify your application when content changes, enabling automatic cache invalidation and other automation.
Webhook Events
When you add a webhook you choose which categories of event it subscribes to:
- Content Publish Events: a content item or page is published or unpublished
- Content Save Events: a content item or page is saved or deleted
- Content Workflow Events: content is requested for approval, approved, or declined
The state value in the payload tells you which specific event occurred.
Webhook Configuration
Step 1: Create Webhook Endpoint
Create a webhook endpoint in your application:
// app/api/revalidate/route.ts
export async function POST(request: Request) {
// Verify the signature (see Securing your webhook, below)
// Process webhook event
// Revalidate cache
return Response.json({ revalidated: true })
}
Step 2: Configure Webhook in Agility CMS
- Navigate to Settings → Webhooks
- Click "Add Webhook"
- Configure webhook:
- URL: Your webhook endpoint URL
- Events: Select which categories of event should trigger it
- Enable secure delivery: recommended — see below
- Enable retries: recommended — see below
Securing your webhook
Your webhook endpoint is a public URL. Turn on secure delivery so your application can prove that a request genuinely came from your Agility instance and was not modified in transit.
When you enable it, Agility generates a signing secret and signs every delivery using the open Standard Webhooks specification. Your endpoint verifies the signature with any off-the-shelf standard-webhooks library.
- Edit the webhook and tick Enable secure delivery
- Save — Agility generates the signing secret and shows it immediately
- Copy it into your application's configuration
- Verify the signature on every request
Full instructions, including verification samples for Node.js, C#, Python and PHP: Verifying Signed Webhooks.
Viewing or rolling a signing secret requires Full Control. Users with a lower permission level can still create and manage webhooks, but the secret is hidden from them.
⚠️ There is no way to secure a webhook with a security key or a custom header. Agility does not send
AGILITY_SECURITY_KEY, or any other shared secret, with a webhook — that key is used for preview authentication and is unrelated. An earlier version of this guide showed a validation step based on it; that check could never succeed, because the header it looked for was never sent. Secure delivery is the way to verify a webhook.
Retries
By default a delivery is attempted once. If your endpoint is briefly unavailable, that delivery is lost.
Tick Enable retries on the webhook to have Agility retry a failed delivery, and choose:
- Retry count — 1 to 8 attempts
- Retry speed —
fast(30 seconds),standard(5 minutes) orslow(30 minutes) base interval, growing exponentially with jitter
Delivery is at-least-once, so design your endpoint to tolerate receiving the same event twice. Use the webhook-id header as an idempotency key — it is unique per event, stable across retries, and sent on every delivery whether signed or not.
Webhook Payload
Content Published Event
{
"state": "Published",
"instanceGuid": "your-instance-guid",
"languageCode": "en-us",
"referenceName": "posts",
"contentID": 204,
"contentVersionID": 1287,
"changeDateUTC": "2025-12-08T15:12:10.883Z"
}
Page Published Event
{
"state": "Published",
"instanceGuid": "your-instance-guid",
"languageCode": "en-us",
"pageID": 2,
"pageVersionID": 114,
"changeDateUTC": "2025-12-08T15:12:10.883Z"
}
Webhook Handler Implementation
Cache Revalidation
export async function POST(request: Request) {
// Read the raw body first if you are verifying the signature —
// re-serializing the JSON will break verification.
const data = await request.json()
// Only process publish events
if (data.state === "Published") {
// Revalidate content tags
if (data.referenceName) {
revalidateTag(`agility-content-${data.referenceName}-${data.languageCode}`)
revalidateTag(`agility-content-${data.contentID}-${data.languageCode}`)
}
// Revalidate page tags
if (data.pageID) {
revalidateTag(`agility-page-${data.pageID}-${data.languageCode}`)
}
// Revalidate paths
if (data.path) {
revalidatePath(data.path)
}
}
return Response.json({ revalidated: true })
}
Webhook Security
Security Best Practices
- Enable secure delivery and verify the signature on every request — see Verifying Signed Webhooks
- Compare signatures in constant time, and reject deliveries whose timestamp is far from your clock
- Be idempotent — use
webhook-idto discard events you have already handled - Return quickly — do the work asynchronously and respond, rather than holding the connection open
- Monitor activity — check delivery history when something looks wrong
Troubleshooting
Start with delivery history
Every webhook has a History action in Settings → Webhooks. It shows each delivery attempt: the status code your endpoint returned, which attempt it was, when the next retry is scheduled, and the payload and response bodies. That is usually faster than reproducing the problem.
Delivery history is go-forward only. A webhook created before this feature shipped shows an empty history until it fires again — that is expected, not a fault.
Webhook Not Firing
Issue: Webhook not receiving events
Solutions:
- Verify the webhook URL is correct and publicly reachable
- Check the webhook is enabled
- Verify the right event categories are selected
- Open History to see whether Agility attempted a delivery and what came back
Signature Verification Failing
Issue: Your endpoint rejects deliveries as unsigned or invalid
Solutions:
- Confirm Enable secure delivery is on for that webhook
- Verify against the raw request body — a JSON body parser that re-serializes the payload will break the signature
- Accept any matching signature in the header: during the 24 hours after a secret roll, two space-separated signatures are sent
- Confirm your stored secret matches the one on the webhook — roll it if in doubt
Next: Troubleshooting - Admin troubleshooting