Developers · Webhooks

Assessment and review events

Webhooks push assessment and review events to your systems so high-risk agent actions reach reviewers, ticketing and your SIEM without polling. Every delivery is signed, retried and traceable.

Configuring an endpoint

Webhook endpoints are configured per organization in the Asyrx control plane. Each endpoint has its own signing secret and its own event subscriptions.

  • Endpoints must be HTTPS and must respond within a few seconds with any 2xx status.
  • Signing secrets are shown once at creation and can be rotated without downtime.
  • Test-mode and live-mode events are delivered separately, so a sandbox integration never receives live tenant events.

Event catalog

The complete set of events Asyrx emits.

EventEmitted when
assessment.createdAn assessment record was created for a proposed action.
assessment.completedAn assessment finished and the full result is available.
assessment.high_riskA completed assessment reached your organization's high-risk threshold.
assessment.criticalA completed assessment reached your organization's critical threshold.
review.recommendedThe assessment recommends human review or secondary approval.
review.completedA reviewer in the Asyrx control plane recorded a review decision.
webhook.failedA previous delivery exhausted its retries.
webhook.testA manual test delivery triggered from your webhook settings.

Delivery headers

Sent on every delivery attempt.

HeaderMeaning
asyrx-signatureVersioned HMAC-SHA256 signature of the delivery, formatted as v1=<hex digest>.
asyrx-timestampUnix timestamp included in the signed payload. Reject deliveries older than five minutes.
asyrx-event-idUnique event identifier. Use it to deduplicate deliveries.
asyrx-event-typeThe event name, e.g. assessment.critical.
asyrx-delivery-attempt1 for the first attempt, incrementing on each retry.
asyrx-request-idTrace identifier for the delivery.

Verifying a signature

Compute an HMAC-SHA256 over the timestamp and the raw request body, using your endpoint secret, and compare in constant time. Always verify before parsing or trusting a payload.

JavaScript

import crypto from "node:crypto";

// The signed payload is: `${timestamp}.${rawBody}`
export function verifyAsyrxWebhook(rawBody, headers, secret) {
  const timestamp = headers["asyrx-timestamp"];
  const signature = headers["asyrx-signature"] ?? "";

  // Reject stale deliveries (replay protection).
  const ageSeconds = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!timestamp || Number.isNaN(ageSeconds) || ageSeconds > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const provided = signature.includes("=") ? signature.split("=")[1] : signature;

  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(provided ?? "", "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Use the exact bytes of the request body — re-serializing the JSON changes the digest. Never log your signing secret and never echo it back in a response.

Handling deliveries

Respond 2xx as soon as the signature is valid, then process the event asynchronously.

JavaScript · Express

// Verify first, respond 2xx fast, process asynchronously.
app.post("/webhooks/asyrx", express.raw({ type: "application/json" }), async (req, res) => {
  const rawBody = req.body.toString("utf8");

  if (!verifyAsyrxWebhook(rawBody, req.headers, process.env.ASYRX_WEBHOOK_SECRET)) {
    return res.status(401).send("invalid signature");
  }

  const event = JSON.parse(rawBody);

  if (await alreadyProcessed(req.headers["asyrx-event-id"])) {
    return res.status(200).send("ok"); // idempotent replay
  }

  res.status(200).send("ok");
  await enqueue(event); // do the work off the request path
});

Retries and ordering

Failed deliveries are retried with exponential backoff. Design your handler to be idempotent and order-independent.

  • Any non-2xx response, timeout or connection error is retried with exponential backoff and increasing asyrx-delivery-attempt values.
  • When retries are exhausted, Asyrx records the failure and emits webhook.failed.
  • Deduplicate on asyrx-event-id: at-least-once delivery means the same event can arrive twice.
  • Events are not ordering-guaranteed. Use created_at and assessment_id to reconcile.