Custom providers
Cover any HMAC-signed webhook with the generic hmac() provider.
Most providers sign the raw body with an HMAC and put it in one header. The hmac() provider covers that whole family: you pick the algorithm (sha1, sha256, sha512), the encoding (hex or base64), an optional prefix like sha256=, and where the event id and type travel:
app/api/webhooks/acme/route.ts
import { webhook, hmac } from "next-webhooks";
export const POST = webhook({
provider: hmac({
name: "acme",
secret: process.env.ACME_WEBHOOK_SECRET!,
header: "x-acme-signature",
prefix: "sha256=",
eventId: (headers) => headers.get("x-acme-delivery"),
eventType: (_headers, payload) =>
(payload as { type?: string } | null)?.type ?? null,
}),
handler: async (event) => {
// ...
},
});Writing a provider from scratch
If a service uses a scheme hmac() cannot express, implement the WebhookProvider interface. verify receives the exact request bytes:
lib/my-provider.ts
import type { WebhookProvider } from "next-webhooks";
const myProvider: WebhookProvider = {
name: "my-service",
async verify(rawBody, headers) {
// rawBody is a Uint8Array of the exact bytes that were signed
const valid = await checkSignature(rawBody, headers);
if (!valid) return { ok: false, reason: "Signature mismatch" };
return { ok: true, id: headers.get("x-delivery-id"), type: null };
},
};