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. The secret is also returned on every subsequent read of the endpoint, so you can re-fetch it later.
- Each delivery includes two headers:
X-Webhook-Event— the event type (e.g.run.completed).X-Webhook-Signature— the stringsha256=followed by the hex-encoded HMAC-SHA256 of the raw request body, keyed with your signing secret.
- Your server strips the
sha256=prefix, computes the expected HMAC over the raw body using the shared secret, and compares the two values with a constant-time comparison.
Verification example
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhookSignature(payload: string, header: string, secret: string): boolean {
if (!header.startsWith('sha256=')) {
return false;
}
const signature = header.slice('sha256='.length);
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 header = req.headers['x-webhook-signature'] as string;
const payload = req.body.toString();
if (!header || !verifyWebhookSignature(payload, header, 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.
- Verify against the raw body. Compute the HMAC over the exact bytes received — re-serializing parsed JSON can change key order or whitespace and break the signature.
- 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.