Setting up webhooks and frontend revalidation

How to configure SCHMA webhooks, verify cryptographic HMAC SHA-256 signatures, and trigger on-demand ISR revalidation in Next.js when content changes.

Developer Guides & Integrations6 min read·Updated October 2026

Setting up webhooks and frontend revalidation

How to configure SCHMA webhooks, verify cryptographic HMAC SHA-256 signatures, and trigger on-demand ISR revalidation in Next.js when content changes.

Before you begin

  • A SCHMA workspace with Admin or Owner permissions (Free supports up to 2 active webhook endpoints, Pro up to 5)
  • A deployed frontend application with an HTTPS webhook endpoint

Webhooks bridge your SCHMA workspace and external services by sending real-time HTTP POST notifications whenever content is created, updated, published, or deleted. In modern static and edge-rendered frontends, webhooks allow you to trigger instant on-demand cache revalidation.

How SCHMA webhooks work

When an event occurs in your workspace, SCHMA constructs a JSON payload containing the event type, entry ID, content model slug, and timestamp. It signs the raw payload using your webhook secret and dispatches an HTTP POST request to your designated endpoint with built-in retry backoff. Free workspaces can register up to 2 active webhook endpoints, while Pro workspaces support up to 5 active webhook endpoints.

Supported webhook event types

You can subscribe your webhook to any combination of five core entry events:

  • entry.created: Emitted when an entry is initially created as a draft.
  • entry.updated: Emitted when changes to an existing draft are saved.
  • entry.published: Emitted when an entry transitions to published status (ideal for frontend cache purging).
  • entry.unpublished: Emitted when a published entry is moved back to draft or unlisted.
  • entry.deleted: Emitted when an entry is permanently removed.

Configuring a webhook in SCHMA Studio

Open your workspace and navigate to Settings → Webhooks:

  1. In the Create Webhook panel, enter your target URL (must begin with https://).
  2. Select the events you want to receive (e.g., entry.published and entry.deleted).
  3. Click Create Webhook.
  4. Copy the generated webhook signing secret (whsec_...) and save it to your server's environment variables (SCHMA_WEBHOOK_SECRET).

Verifying HMAC SHA-256 signatures

To verify that incoming webhook requests originate from SCHMA and have not been tampered with, SCHMA sends a cryptographic signature in the x-schma-signature header. The signature is an HMAC SHA-256 hex digest of the raw request body computed with your shared secret.

Next.js on-demand revalidation handler example

Here is a complete Next.js Route Handler (app/api/revalidate/route.ts) demonstrating secure signature verification and on-demand cache revalidation:

app/api/revalidate/route.ts
import crypto from "crypto";
import { revalidatePath, revalidateTag } from "next/cache";

export async function POST(request: Request) {
  const secret = process.env.SCHMA_WEBHOOK_SECRET;
  if (!secret) {
    return new Response("Webhook secret not configured", { status: 500 });
  }

  const rawBody = await request.text();
  const signature = request.headers.get("x-schma-signature");

  if (!signature) {
    return new Response("Missing signature header", { status: 400 });
  }

  // Calculate HMAC SHA-256 digest
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  // Constant-time comparison to prevent timing attacks
  const isMatch = crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );

  if (!isMatch) {
    return new Response("Invalid webhook signature", { status: 401 });
  }

  const payload = JSON.parse(rawBody);

  // If entry was published or deleted, revalidate paths
  if (payload.event === "entry.published" || payload.event === "entry.deleted") {
    revalidatePath(`/${payload.typeSlug}/${payload.slug}`);
    revalidateTag("content");
  }

  return Response.json({ revalidated: true, event: payload.event });
}