Skip to main content

Webhook Signatures

Every webhook delivery is signed with your webhook's secret so you can verify it came from Vidocu.

Signature format

The signature is sent in the X-Vidocu-Signature header:

X-Vidocu-Signature: sha256=a1b2c3d4e5f6...

Verification algorithm

  1. Get the timestamp from X-Vidocu-Timestamp
  2. Get the raw request body as a string
  3. Concatenate: {timestamp}.{body}
  4. Compute HMAC SHA-256 using your webhook secret as the key
  5. Compare with the signature (after removing the sha256= prefix)

Code examples

Node.js

import crypto from "crypto";

function verifyWebhook(
body: string,
signature: string,
timestamp: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${body}`)
.digest("hex");

const received = signature.replace("sha256=", "");

return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(received)
);
}

Python

import hmac
import hashlib

def verify_webhook(body: str, signature: str, timestamp: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()

received = signature.removeprefix("sha256=")

return hmac.compare_digest(expected, received)

Express middleware example

import express from "express";
import crypto from "crypto";

const app = express();

app.post(
"/webhooks/vidocu",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["x-vidocu-signature"] as string;
const timestamp = req.headers["x-vidocu-timestamp"] as string;
const body = req.body.toString();

const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(`${timestamp}.${body}`)
.digest("hex");

const received = signature.replace("sha256=", "");

if (
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))
) {
return res.status(401).send("Invalid signature");
}

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

// Process the event...

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

Security tips

  • Always verify signatures before processing events
  • Use timingSafeEqual (or equivalent) to prevent timing attacks
  • Reject requests with timestamps older than 5 minutes to prevent replay attacks
  • Use HTTPS for your webhook endpoint