Developers
Webhooks
Subscribe an https endpoint to audit events instead of polling. Manage endpoints with the webhooks:manage scope via POST /api/v1/webhooks; the signing secret is returned once at creation.
Events
run.completed: an audit finished. Payload carries runId, score, severity counts, and API plus dashboard links.run.failed: an audit failed terminally (after retries).monitoring.changed: a monitored page changed, recovered, or failed a check. Payload carries projectId, url, kind, and httpStatus.
Delivery format
POST <your endpoint>
content-type: application/json
x-index365-event: run.completed
x-index365-event-id: evt_3f2a... # idempotency key: dedupe on this
x-index365-signature: t=1765400000,v1=<hex hmac>
{"id":"evt_3f2a...","type":"run.completed","createdAt":"...","data":{...}}Verify the signature
The signature is HMAC-SHA256 over <t>.<raw body> with your endpoint secret. Reject timestamps older than 5 minutes to block replays, and compare in constant time.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, rawBody, header) {
const match = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? "");
if (!match) return false;
const [, t, signature] = match;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(signature, "hex"));
}Retries and idempotency
- Respond with any 2xx within 5 seconds. Do heavy work asynchronously after acknowledging.
- Failed deliveries retry on a bounded backoff (1m, 5m, 30m, 2h, 6h, 24h), then stop.
- Deliveries can arrive more than once across retries: deduplicate on
x-index365-event-id. - Endpoints must be https. Disable an endpoint by setting it inactive via PATCH.