Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vouchmark.com/llms.txt

Use this file to discover all available pages before exploring further.

Webhooks push events to your server as they happen, so you don’t have to poll. Configure one or more endpoints in the dashboard at Settings → Webhooks. Each endpoint can subscribe to a subset of event types.

Event types

EventWhen it fires
verification.completedAny verification on a company finishes (success or failure).
company.score_updatedA company’s Vouchmark Score changes.
badge.issuedA Trust Badge has been minted for one of your companies.
badge.revokedA badge has been revoked (sanction, score drop, manual).
monitoring.event_createdA new Smart Sentinel event lands on a vendor you monitor.
monitoring.alert_createdA new alert in your inbox.

Payload shape

Every webhook delivery is a POST with this body:
{
  "id": "evt_aBcDeFgH...",
  "type": "verification.completed",
  "createdAt": "2026-05-12T09:14:00Z",
  "data": {
    "companyId": "cmp_aBcD...",
    "verification": "firsTin",
    "status": "verified",
    "result": {
      "firsTin": "2522576963525",
      "taxpayerName": "TEKCIFY TECHNOLOGIES LTD"
    }
  }
}
The data field depends on the event type — see the dashboard’s webhook playground for live examples.

Signature verification

We sign every delivery with the webhook’s signing secret (visible once at creation). The signature is an HMAC-SHA256 over the raw request body, in the X-Vouchmark-Signature header:
X-Vouchmark-Signature: t=1715508840,v1=4f3a...
Verify it in your handler:
import crypto from "node:crypto";

function isValid(rawBody: string, header: string, secret: string): boolean {
  const [tsPart, sigPart] = header.split(",");
  const ts = tsPart.split("=")[1];
  const sig = sigPart.split("=")[1];
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");
  return (
    Math.abs(Date.now() / 1000 - Number(ts)) < 300 &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
  );
}
Reject deliveries older than 5 minutes (the timestamp window above). This prevents replay.

Delivery, retries, and ordering

  • We retry failed deliveries with exponential backoff: 1m, 5m, 30m, 2h, 6h, 24h. After 24h with no 2xx response we drop the delivery and surface it in the dashboard.
  • Deliveries are at-least-once. Build idempotent handlers keyed on the id field.
  • Order is not guaranteed across events. If you need a strict view, refetch the resource on each event.

Testing

The dashboard’s webhook playground lets you fire test deliveries to any endpoint without affecting production data.