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.