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.
| Event | Emitted when |
|---|---|
| assessment.created | An assessment record was created for a proposed action. |
| assessment.completed | An assessment finished and the full result is available. |
| assessment.high_risk | A completed assessment reached your organization's high-risk threshold. |
| assessment.critical | A completed assessment reached your organization's critical threshold. |
| review.recommended | The assessment recommends human review or secondary approval. |
| review.completed | A reviewer in the Asyrx control plane recorded a review decision. |
| webhook.failed | A previous delivery exhausted its retries. |
| webhook.test | A manual test delivery triggered from your webhook settings. |
Delivery headers
Sent on every delivery attempt.
| Header | Meaning |
|---|---|
| asyrx-signature | Versioned HMAC-SHA256 signature of the delivery, formatted as v1=<hex digest>. |
| asyrx-timestamp | Unix timestamp included in the signed payload. Reject deliveries older than five minutes. |
| asyrx-event-id | Unique event identifier. Use it to deduplicate deliveries. |
| asyrx-event-type | The event name, e.g. assessment.critical. |
| asyrx-delivery-attempt | 1 for the first attempt, incrementing on each retry. |
| asyrx-request-id | Trace 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-attemptvalues. - 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_atandassessment_idto reconcile.
