Webhooks

Subscribe to platform events, verify signatures, handle retries, and configure endpoints from the tenant app.

7 min readArticle 1 of 2 in Developers & Integrations

Webhooks

Webhooks let you react to events in your AUDIGYD workspace by receiving an HTTPS POST every time something interesting happens — an assessment is finalized, a finding is resolved, a report is ready.

Configuring an endpoint

1.Open the tenant app and go to the Webhooks page (Settings → Webhooks; requires the settings:manage_settings permission, which is held by Owner by default).
2.Click Add Endpoint.
3.Enter:
  • Name — a short label, used in the endpoint list and audit logs
  • URL — your HTTPS receiver
  • Events — pick one or more event types from the catalogue below
  • 4.Save. AUDIGYD generates a signing secret in the form whsec_<64 hex chars> and shows it once in a banner that reads "Copy your signing secret now — it won't be shown again". Copy it before navigating away.

    From the same page you can pause / re-activate (toggle isActive), edit the endpoint's name and event subscriptions, send a test event, inspect deliveries (with a per-delivery Resend button), and delete an endpoint.

    Event catalogue

    The complete set of event types you can subscribe to today (returned by GET /api/t/:tenantId/webhooks/events):

    EventWhen it firesNotes
    assessment.createdA new assessment is createdIncludes id, name, status
    assessment.submittedA respondent submits an assessment
    assessment.finalizedReviewer finalizes an assessmentPayload includes the scoreEnvelope
    assessment.score_updatedScore envelope recomputed
    finding.createdNew finding is created (during review or manually)GRC-relevant
    finding.status_changedFinding status moves (e.g. open → acknowledged → resolved → accepted_risk)GRC-relevant
    finding.resolvedFinding transitions to resolvedGRC-relevant
    report.generatedA report is generated
    report.readyA report finishes processing and is downloadable

    > Reacting to GRC changes. The table above is the source of truth for what is dispatched as a webhook today. The finding.* events are the primary GRC integration surface — when teams workflow evidence, policy, or risk decisions through findings, those decisions become observable as finding lifecycle transitions (open → acknowledged → resolved → accepted_risk). For evidence, policy, risk, and control-readiness state that is not mirrored through a finding, query the corresponding REST endpoints (e.g. GET /api/t/:tenantId/evidence, /policies, /risks, /controls) on a schedule, or use the MCP server (next article) to ask a model for changes since a given timestamp.

    Payload shape

    Every delivery is a JSON POST with this envelope:

    {
    
      "id": "1f8e92c0-2b1a-4d4e-9b66-2f9b5a76b7f0",
    
      "event": "assessment.finalized",
    
      "tenant_id": "ten_01HXYZ...",
    
      "created_at": "2026-04-16T18:22:31.412Z",
    
      "data": { ... event-specific fields ... }
    
    }
    
    

    Example finding.status_changed data block:

    {
    
      "id": "fnd_01J...",
    
      "title": "Encryption-at-rest not documented",
    
      "previousStatus": "open",
    
      "status": "resolved",
    
      "severity": "high"
    
    }
    
    

    Request headers

    Every delivery includes:

    • Content-Type: application/json
    • User-Agent: AUDIGYD-Webhooks/1.0
    • X-AUDIGYD-Event — the event name (e.g. assessment.finalized)
    • X-AUDIGYD-Signature — HMAC-SHA256 of the raw request body, hex-encoded; see the next section for the exact key

    Verifying the signature

    > Important: AUDIGYD computes the HMAC using the SHA-256 hash of your signing secret as the key (not the raw whsec_... value). To verify, derive the same key in your code: hashKey = sha256_hex(rawSecret), then HMAC_SHA256(hashKey, rawRequestBody). Use a constant-time comparison against X-AUDIGYD-Signature.

    // Node.js (Express)
    
    import crypto from "crypto";
    
    
    
    const RAW_SECRET = process.env.AUDIGYD_WEBHOOK_SECRET!;        // whsec_...
    
    const HASH_KEY = crypto.createHash("sha256").update(RAW_SECRET).digest("hex");
    
    
    
    app.post("/webhooks/audigyd", express.raw({ type: "application/json" }), (req, res) => {
    
      const sig = req.header("X-AUDIGYD-Signature") ?? "";
    
      const expected = crypto
    
        .createHmac("sha256", HASH_KEY)
    
        .update(req.body)
    
        .digest("hex");
    
    
    
      const ok =
    
        sig.length === expected.length &&
    
        crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
    
      if (!ok) return res.status(401).end();
    
    
    
      const event = JSON.parse(req.body.toString());
    
      // handle event...
    
      res.status(200).end();
    
    });
    
    

    The raw whsec_... secret is what you store; AUDIGYD only ever persists its SHA-256 hash, and that hash is the value used to sign every delivery. To replace a secret today, delete the endpoint and create a new one — there is no in-place rotation flow yet.

    Retries & backoff

    • A delivery is successful when your endpoint returns an HTTP 2xx within 10 seconds.
    • Failed deliveries (non-2xx, timeout, or network error) are retried up to 5 attempts total (1 initial + 4 retries). The wait between attempts is 5m → 30m → 2h → 6h.
    • After 5 consecutive endpoint-level failures, the endpoint is automatically deactivated and an email is sent to the tenant's contact email. Re-activate it after fixing the issue.
    • You can re-trigger any delivery manually from Webhooks → Deliveries → Resend.

    Idempotency

    The envelope id is unique per delivery (a fresh UUID is generated on every attempt). To deduplicate retries, store the event-specific data.id alongside the event name and skip if you have already processed it.

    Tips

    • Always return 2xx quickly and queue the work asynchronously — long handlers cause spurious timeouts and retries.
    • Use a tunnelling tool (e.g. ngrok) to receive deliveries during local development, then promote to a real HTTPS URL.