@trueyy-sdk/node

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 missing x-trueyy-event-id / x-trueyy-event-type headers.
  • 400 - raw body unavailable or not JSON.

Event catalog

Every event shares the envelope { event_id, event_type, created_at, api_version, data }.

event_typeFires whenSample data
session.readyCandidate + helper online and ready{ session_id, ... }
session.transcript_segmentA final transcript fragment is produced{ session_id, speaker, text, started_at, ended_at }
session.risk_pulseA risk alert fires (AI tool detected, paste threshold){ session_id, kind, severity, evidence }
session.window_resultA 30-second analysis window completes{ session_id, window_id, risk, score, summary, modality_breakdown }
session.image_analysis_resultVision LLM completes screenshot analysis{ session_id, image_id, risk, findings }
session.endedA round moves to ENDED (any path){ session_id, ... }
session.report_readyThe round's analysis report is generated{ session_id, ... }
session.cancelledCancelled 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.

On this page

Ask ChatGPT