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

  1. Navigate to SettingsWebhooks
  2. Click "Add Webhook"
  3. 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.

  1. Edit the webhook and tick Enable secure delivery
  2. Save — Agility generates the signing secret and shows it immediately
  3. Copy it into your application's configuration
  4. 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 speedfast (30 seconds), standard (5 minutes) or slow (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

  1. Enable secure delivery and verify the signature on every request — see Verifying Signed Webhooks
  2. Compare signatures in constant time, and reject deliveries whose timestamp is far from your clock
  3. Be idempotent — use webhook-id to discard events you have already handled
  4. Return quickly — do the work asynchronously and respond, rather than holding the connection open
  5. 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