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.