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