Webhooks
Alert rules deliver over HTTP with an HMAC signature. How to verify one, what the retry policy is, and why a replay window matters more than it sounds.
Webhooks
Vorim POSTs to your endpoint when an audit event matches an alert rule you have configured. The body carries the rule that matched and the event that matched it, so you can route on either without a second lookup.
What arrives
Four headers travel with every delivery. Content-Type is application/json, X-Vorim-Event repeats the event type so you can route before parsing, X-Vorim-Timestamp is the ISO 8601 time the body was signed, and X-Vorim-Signature carries the HMAC. The signature header is present only when the deployment has a signing secret configured.
POST https://your-endpoint.example.com/vorim
Content-Type: application/json
X-Vorim-Event: tool_call
X-Vorim-Timestamp: 2026-08-31T09:14:02.118Z
X-Vorim-Signature: sha256=9f2b...c41d
{
"rule_id": "rule_7f3a2b",
"rule_name": "Denied transactions",
"event": {
"event_type": "tool_call",
"agent_id": "agid_acme_b5f35578",
"action": "invoice.refund",
"result": "denied"
},
"timestamp": "2026-08-31T09:14:02.118Z"
}typescriptVerify the signature
Recompute the HMAC and compare it in constant time. Verify against the raw request body, never against JSON you have parsed and re-serialised, because a round trip through your JSON library will reorder keys or change number formatting and the MAC will not match.
Reject anything whose timestamp is more than 300 seconds from now. That window is wide enough for ordinary clock skew and narrow enough to make a replayed delivery useless.
import { createHmac, timingSafeEqual } from 'node:crypto';
// You need the RAW body: a JSON round trip changes the bytes the MAC covers.
app.post('/vorim', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-Vorim-Timestamp') ?? '';
const received = req.get('X-Vorim-Signature') ?? '';
const age = Math.abs(Date.now() - Date.parse(timestamp)) / 1000;
if (!timestamp || Number.isNaN(age) || age > 300) return res.sendStatus(400);
const expected = 'sha256=' + createHmac('sha256', process.env.VORIM_WEBHOOK_SECRET)
.update(timestamp + '.' + req.body.toString('utf8'))
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(received);
if (a.length !== b.length || !timingSafeEqual(a, b)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString('utf8'));
res.sendStatus(200); // acknowledge fast, process out of band
});typescriptWhat we do not promise
Book a demo for a walkthrough, or contact us for support. For enterprise needs, reach out at sales@vorim.ai.