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.