Skip to main content

Verify Webhooks

When Genuka sends webhook events to your application, it is important to verify that the request is genuine and has not been tampered with.
We achieve this by signing each webhook request with a secret key associated with your application.

Signature Header

Every webhook request from Genuka includes a special header:

X-Genuka-Signature

This header contains a hash signature generated with your Webhook Secret (provided when you create your application in the Genuka Developer Dashboard).

Verification Process

  1. Retrieve the raw request body exactly as received.
  2. Compute a HMAC SHA256 hash using your Webhook Secret.
  3. Compare your computed hash with the value in the X-Genuka-Signature header.
    • If they match → the request is authentic.
    • If not → discard the request.

Example in Node.js (Next.js App Router)

// app/api/webhook/route.ts
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";

const WEBHOOK_SECRET = process.env.GENUKA_WEBHOOK_SECRET!;

function verifySignature(rawBody: string, signature: string): boolean {
  const computed = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(rawBody, "utf8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(computed),
    Buffer.from(signature)
  );
}

export async function POST(req: NextRequest) {
  const signature = req.headers.get("x-genuka-signature");
  const rawBody = await req.text(); // must read as text for exact raw body

  if (!signature || !verifySignature(rawBody, signature)) {
    return NextResponse.json(
      { error: "Invalid signature" },
      { status: 401 }
    );
  }

  const event = JSON.parse(rawBody);

  switch (event.event) {
    case "order.created":
      console.log("✅ New order received:", event.data);
      break;
    case "order.completed":
      console.log("🎉 Order completed:", event.data);
      break;
    default:
      console.log("⚠️ Unhandled event:", event.event);
  }

  return NextResponse.json({ received: true }, { status: 200 });
}
ErrorCauseSolution
401 UnauthorizedSignature invalid or missingEnsure you are using the correct Webhook Secret
400 Bad RequestMalformed payloadCheck that your endpoint reads the raw request body properly
Request not receivedServer offline or URL incorrectVerify your deployment and webhook URL in the Genuka dashboard