Webhooks
Verify signatures, parse events, and handle Trueyy's delivery guarantees.
Trueyy delivers events to your backend as they happen. The SDK ships Express-compatible middleware that validates the signature, rejects replays, and parses the event for you.
webhooks.verify(secret)
Returns Express middleware. It validates X-Trueyy-Signature (HMAC-SHA256 over the raw body), rejects replays (timestamp older than 5 minutes), parses the body, and attaches req.trueyy.event.
Mount after express.raw()
The HMAC is computed over the raw request bytes. Mount express.raw({ type: "application/json" }) before the verify middleware, or signature validation will fail.
import express from "express";
app.post(
"/webhooks/trueyy",
express.raw({ type: "application/json" }), // MUST come first - raw bytes for HMAC
trueyy.webhooks.verify(WEBHOOK_SECRET),
async (req, res) => {
const event = req.trueyy!.event;
// event = { event_id, event_type, created_at, api_version, data }
// 1. Idempotency check on event_id
if (await alreadySeen(event.event_id)) return res.sendStatus(200);
// 2. Dispatch by event_type
switch (event.event_type) {
case "session.ended":
await persistFinalReport(event.data);
break;
case "session.risk_pulse":
await alertsPipeline.push(event.data);
break;
// ...
}
// 3. Acknowledge - anything non-2xx triggers Trueyy's retry chain.
res.sendStatus(200);
}
);The middleware responds:
401- missing/invalid signature, or missingx-trueyy-event-id/x-trueyy-event-typeheaders.400- raw body unavailable or not JSON.
Event catalog
Every event shares the envelope { event_id, event_type, created_at, api_version, data }.
event_type | Fires when | Sample data |
|---|---|---|
session.ready | Candidate + helper online and ready | { session_id, ... } |
session.transcript_segment | A final transcript fragment is produced | { session_id, speaker, text, started_at, ended_at } |
session.risk_pulse | A risk alert fires (AI tool detected, paste threshold) | { session_id, kind, severity, evidence } |
session.window_result | A 30-second analysis window completes | { session_id, window_id, risk, score, summary, modality_breakdown } |
session.image_analysis_result | Vision LLM completes screenshot analysis | { session_id, image_id, risk, findings } |
session.ended | A round moves to ENDED (any path) | { session_id, ... } |
session.report_ready | The round's analysis report is generated | { session_id, ... } |
session.cancelled | Cancelled before start | { session_id, ... } |
Delivery guarantees
At-least-once
Delivered at least once within ~15 hours. Make your side exactly-once by idempotency-checking event_id.
Retry schedule
1m → 5m → 30m → 2h → 12h, then dead-lettered.
Auto-disable
After 50 consecutive failures your endpoint auto-disables and we email the admin.
Re-fire
Replay dead-lettered events from the dashboard - the original event_id is preserved.