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

  1. When you create a webhook, Tracore generates a signing secret and returns it in the response.
  2. Each delivery includes a x-tracore-signature header containing the HMAC-SHA256 signature of the request body.
  3. 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 200 response before doing heavy processing. Use a queue if your handler takes more than a few seconds.