Skip to main content

How It Works

Webhooks let you receive automatic notifications when events occur in Veridox, such as a file analysis completing.
1

Create a webhook configuration

An organisation owner creates a webhook configuration via the API, providing a destination URL where you want to receive events.
2

Store your signing secret

Veridox provisions a signing secret (format: whsec_{32-char-alphanumeric}) and returns it once in the creation response. After that, only a prefix is shown for identification.
The secret is only shown once at creation time. Store it securely — you cannot retrieve it again.
3

Receive events

When events occur, Veridox sends a POST request to your URL with:
  • A JSON payload in the body
  • An x-vdx-signature header containing an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret
4

Verify and process

You verify the signature on your server to confirm the request genuinely came from Veridox and hasn’t been tampered with, then process the event.

Event Types

Event typeDescription
file.enrichment.successFile analysis completed successfully
file.enrichment.failedFile analysis encountered an error

Payload Structure

When a file analysis completes successfully, Veridox delivers a payload like this:
{
  "event_type": "file.enrichment.success",
  "file_id": "019e2a1b-7c3d-7f00-8a1b-2c3d4e5f6a7b",
  "case_id": "019e2a1b-5b2c-7e00-9d8c-7b6a5f4e3d2c",
  "started_at": "2026-02-17T10:30:00.000Z",
  "completed_at": "2026-02-17T10:30:42.000Z",
  "version": "2.0.0",
  "file_info": {
    "md5": "d41d8cd98f00b204e9800998ecf8427e",
    "mimetype": "application/pdf"
  },
  "results": {
    "overall_risk_score": "high",
    "summary": "Multiple indicators of document manipulation detected...",
    "findings": ["..."],
    "observations": ["..."],
    "risk_assessments": ["..."],
    "suggested_actions": ["..."],
    "audit_trail": ["..."]
  },
  "modules": [
    {
      "module_id": "metadata_analysis",
      "module_name": "Metadata Analysis",
      "findings": ["..."],
      "grounding_sources": ["..."]
    }
  ],
  "artifacts": {
    "report_pdf": "reports/019e2a1b-7c3d-7f00.../report.pdf",
    "report_log": "reports/019e2a1b-7c3d-7f00.../report.log"
  },
  "analysis_metadata": {
    "...": "..."
  }
}
For file.enrichment.failed events, the payload includes an error field instead of results.

Signature Verification

The signature is computed as:
HMAC-SHA256(raw_request_body, your_webhook_secret) → hex string
You compare the x-vdx-signature header value against your own computed HMAC. Always use a timing-safe comparison to prevent timing attacks.
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";

const WEBHOOK_SECRET = "whsec_your_secret_here";

function verifySignature(body, signature) {
  if (!signature) return false;
  const expected = createHmac("sha256", WEBHOOK_SECRET)
    .update(body)
    .digest("hex");
  const sigBuf = Buffer.from(signature, "utf8");
  const expBuf = Buffer.from(expected, "utf8");
  if (sigBuf.length !== expBuf.length) return false;
  return timingSafeEqual(sigBuf, expBuf);
}

createServer(async (req, res) => {
  if (req.method !== "POST") {
    res.writeHead(404).end();
    return;
  }

  const chunks = [];
  for await (const chunk of req) chunks.push(chunk);
  const body = Buffer.concat(chunks);

  if (!verifySignature(body, req.headers["x-vdx-signature"])) {
    res.writeHead(401, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ error: "Invalid signature" }));
    return;
  }

  const event = JSON.parse(body.toString("utf8"));
  console.log("Verified webhook event:", event);

  // Process the event...

  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ ok: true }));
}).listen(3000, () => console.log("Webhook receiver on :3000"));

Best Practices

Store secrets securely — the signing secret is shown once at creation time and cannot be retrieved again
Always verify signatures — validate every incoming request before processing the payload
Use timing-safe comparison — use timingSafeEqual (Node.js) or hmac.compare_digest (Python) to prevent timing attacks
Return 2xx quickly — acknowledge receipt immediately and do heavy processing asynchronously
Only one active webhook configuration is allowed per organisation at a time. Creating a new configuration deactivates the previous one.