Skip to main content
Home›Docs›Webhooks›Verifying webhook signatures
Webhooks

Verifying webhook signatures

Verify the HMAC-SHA256 signature on every Perceive8 webhook delivery before processing it.

Verifying webhook signatures

Perceive8 includes a signature header with every webhook delivery so you can verify that the request came from Perceive8 and was not tampered with.

Header: X-Perceive8-Signature

The signature is an HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret (returned when you create the webhook).

Verification example (Node.js)

import { createHmac } from "crypto";

function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  // Use a timing-safe comparison
  return expected === signature;
}

// Express handler
app.post("/webhooks/perceive8", (req, res) => {
  const sig = req.headers["x-perceive8-signature"];
  const rawBody = req.rawBody; // Ensure body parser preserves raw bytes

  if (!verifyWebhookSignature(rawBody, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(rawBody);
  console.log("Received event:", event.event);

  res.status(200).send("OK");
});

Verification example (Python)

import hashlib
import hmac
from flask import Flask, request

app = Flask(__name__)
WEBHOOK_SECRET = b"your-webhook-secret"

@app.route("/webhooks/perceive8", methods=["POST"])
def webhook():
    sig = request.headers.get("X-Perceive8-Signature", "")
    raw = request.get_data()

    expected = hmac.new(WEBHOOK_SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return "Invalid signature", 401

    event = request.json
    print("Received:", event["event"])
    return "OK", 200

Important: Always validate the signature before processing the webhook payload. Reject requests with missing or invalid signatures.