Verify Webhook Requests
Every webhook delivery is signed with an HMAC-SHA256 signature so you can verify that requests originate from Tracore and have not been tampered with.
How it works
- When you create a webhook, Tracore generates a signing secret and returns it in the response.
- Each delivery includes a
x-tracore-signatureheader containing the HMAC-SHA256 signature of the request body. - Your server computes the expected signature using the shared secret and compares it to the header value.
Verification example
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = createHmac("sha256", secret)
.update(payload)
.digest("hex");
const expectedBuffer = Buffer.from(expected, "hex");
const signatureBuffer = Buffer.from(signature, "hex");
if (expectedBuffer.length !== signatureBuffer.length) {
return false;
}
return timingSafeEqual(expectedBuffer, signatureBuffer);
}
Usage in an Express handler
import express from "express";
const app = express();
app.post("/webhooks/tracore", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-tracore-signature"] as string;
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(payload);
console.log("Received event:", event.event);
// Handle the event
switch (event.event) {
case "run.completed":
// Process extracted data
break;
case "run.failed":
// Handle failure
break;
}
res.status(200).send("OK");
});
Best practices
- Always verify signatures. Never process webhook payloads without checking the signature first.
- Use
timingSafeEqual. A constant-time comparison prevents timing attacks against the signature check. - Store secrets securely. Keep your webhook signing secret in environment variables, not in source code.
- Return 200 quickly. Acknowledge the webhook with a
200response before doing heavy processing. Use a queue if your handler takes more than a few seconds.