Webhooks
Webhooks push events to your endpoint as they happen — a scan finishing, a regression, a VPAT going live — so you don’t have to poll.
Subscribe
Create an endpoint in the dashboard (Settings → Webhooks) or via the API. Each endpoint gets a signing secret, shown once. Delivery is an at-least-once, signed POST with a JSON body.
Event types
| Event | Fires when |
|---|---|
scan.completed | A scan finishes successfully |
scan.failed | A scan errors or times out |
scan.scheduled_due | A scheduled scan is due — your runner/Action can pick it up |
scan.regressed | A scan regressed vs. the previous run of the same schedule |
scan.hosted_enqueued | A hosted scan was queued for the runner fleet |
vpat.finding_reviewed | A reviewer confirmed or overrode a criterion |
vpat.published | A VPAT version was published |
findings.submitted | Findings were ingested for a product |
cache.invalidated | An intent cache was invalidated (e.g. a deploy) |
capability.invoked / capability.failed | A live-assist capability ran or failed |
quota.exceeded | An org hit a rate/usage limit |
member.role_changed / member.removed | Team membership changed |
invite.created / invite.accepted | An invite was sent or accepted |
sso.auto_join | A user auto-joined an org via SSO domain match |
Verify the signature
Every delivery carries three headers:
x-wholisphere-signature: t=<unix-seconds>,v1=<hmac-sha256-hex>x-wholisphere-event-id: <uuid>x-wholisphere-event-type: scan.completedThe signature is HMAC-SHA256(secret, "<t>.<raw-body>"). Verify over the raw request bytes, before JSON parsing. The SDK does this with a constant-time comparison. verifyWebhook is async (it uses SubtleCrypto) — always await it, or the try/catch won’t catch verification failures:
import { verifyWebhook, WebhookVerificationError } from '@wholisphere.ai/sdk';
try { const event = await verifyWebhook({ secret: process.env.WHOLISPHERE_WEBHOOK_SECRET!, signature: req.headers['x-wholisphere-signature'], payload: rawBody, }); // event.type, event.data …} catch (e) { if (e instanceof WebhookVerificationError) return res.status(400).end(); throw e;}Reject anything that fails verification, and treat a stale t (older than a few minutes) as replay.
Delivery + retries
Deliveries retry with exponential backoff (roughly 60s → 4h across 5 attempts) and land in a dead-letter after the final failure. Return a 2xx quickly (within a few seconds); do heavy work asynchronously. Deduplicate on x-wholisphere-event-id — the same event may be delivered more than once.