Webhooks

Register a webhook (an automatic message Aegis posts to a web address you choose) so another system hears about an event — a policy approved, a risk mitigated, an incident resolved — the moment it happens.

A webhook saves someone the job of signing in to check whether anything has changed. You give Aegis a URL, tick the events you care about, and set a shared secret; each time a matching event occurs, Aegis posts a small JSON message to that URL and the receiving system decides what to do with it. Webhooks are outbound only — they push information out of Aegis and never carry anything back in. Webhook Settings is the one screen where an administrator registers endpoints, switches them on and off, tests them, and reads what was delivered.

Administrator only, and part of some plans only

Every webhook action is restricted to administrators. The endpoint list is also gated behind the Webhook Integrations licence feature: if your plan does not include it, the page still opens, but in place of the list you get an amber padlock, the heading Webhook Integrations, a line explaining that the feature is not on your current plan, and a View Plans button that takes you to /settings/license.

Who uses it

Roles and what each can do lists the other administrator-only areas. The shorter address /webhooks brings an administrator here as well; on a plan without the feature it shows the same locked notice instead.

What's on this screen

The page is short and has one job. At the top sits a breadcrumb reading Settings / Webhooks, where Settings links back to the settings index; under it the heading Webhook Settings and the line Manage outbound webhook integrations for real-time event notifications to external systems.

Below that is a single white card titled Webhook Endpoints, subtitled Configure outbound webhooks to notify external systems when events occur in Aegis. A dark blue Add Webhook button sits in its top-right corner, and everything else on the screen appears inside that card — no filter bar, no search, no sorting, because the list is expected to stay short.

In the screenshot below the card is empty, because the workspace it was captured on has no endpoints yet: a grey circle with a plus sign, the line No webhooks configured, and beneath it Create your first webhook to start receiving event notifications. That is the normal starting point for a new organisation.

Once endpoints exist, each becomes a row in the same card. From left to right a row shows a status pill (Active with a tick, or Disabled with a cross), the description if you gave one, the destination URL in a monospaced font, and small grey tags — one per subscribed event. On the right sit its five controls: an on/off switch (green when the endpoint is active, grey when it is not), a play icon that sends a test delivery, a pencil icon that opens the edit form, a bin icon that deletes the endpoint, and a History button that expands the delivery log underneath.

Around the card is the usual application frame: the left navigation, whose ADMINISTRATION group holds Webhooks, and the top bar, which carries the help ? and your account name — the two controls the steps below use.

Opening Webhook Settings

  1. Select the ? in the top bar whenever you want these instructions to hand. Aegis opens this User Guide in the same tab, already at the chapter for the screen you were on; your browser's back button returns you to the screen.
  2. Check the name in the top-right corner is the account you mean to use. Webhook Settings opens only for an administrator; any other role lands on the /unauthorized page instead.
  3. In the left navigation, select ADMINISTRATION to expand that group, then choose Webhooks. The page loads at /settings/webhooks with the Settings / Webhooks breadcrumb and the Webhook Endpoints card — a grey placeholder stands in while the list is fetched. The Settings index links here too.
Webhook Settings on a workspace with no endpoints yet — the Add Webhook button sits in the top-right of the Webhook Endpoints card — /settings/webhooks.
Webhook Settings on a workspace with no endpoints yet — the Add Webhook button sits in the top-right of the Webhook Endpoints card — /settings/webhooks.

Add an endpoint

Have the receiving URL ready, and decide on a signing secret — a long random string both sides hold. Aegis signs every message with it, so the receiver can prove the message came from Aegis and was not altered on the way.

  1. Select Add Webhook in the top-right of the card. A dialog opens titled Add Webhook, with four fields and a Cancel / Create Webhook pair at the bottom.
  2. In Endpoint URL (required), type the full address Aegis should post to — the field shows https://example.com/webhooks/aegis as its example. Anything that is not a valid http or https URL draws Invalid URL format or URL must use HTTP or HTTPS protocol in red under the field, which clears as you correct it. On a production deployment the server accepts https only, replying Webhook URL must use HTTPS.
  3. In Description (optional), add a short label such as CI/CD pipeline notifications — it is what identifies the row in the list later.
  4. In Signing Secret (required when adding), type your secret. The field is masked and the hint below reads Used to generate HMAC-SHA256 signatures for payload verification. Store the same value on the receiving side; Aegis never shows it back to you.
  5. Under Event Types (at least one required), tick the events this endpoint should receive. Ticking a group heading selects the whole group, and that heading shows a partial state when only some of its events are ticked. Submitting with nothing ticked shows Select at least one event type.
  6. Select Create Webhook. The button reads Saving…, then the dialog closes, Webhook created successfully appears, and the new endpoint replaces the empty state as an Active row. Cancel keeps nothing. On save Aegis checks the address before it stores anything: the URL must be HTTPS and its host name must already resolve to a public internet address. No delivery is sent at this point, so a receiver that is not running yet is accepted; use Send test afterwards to prove it answers. If the host name does not resolve yet (for example the DNS record is still to be created) or points into a private network, the dialog stays open and the URL field reads Aegis could not accept this address with what to fix; create the DNS record first, then save again.

The events you can subscribe to

The list is fixed — these are the events Aegis can send today, and the dialog offers no others. Each is named area.action.

Group Events
Policy policy.created, policy.updated, policy.approved, policy.acknowledged
Risk risk.created, risk.updated, risk.mitigated
Vendor vendor.created, vendor.assessed
Evidence evidence.uploaded, evidence.expired, evidence.expiring
Incident incident.created, incident.resolved
Compliance compliance.score_changed, control.status_changed
ActionItem action_item.due_soon, action_item.overdue

What arrives at your endpoint

Aegis sends an HTTP POST with Content-Type: application/json. Whoever builds the receiver should check the signature, then reply with a 2xx status code. Aegis waits about ten seconds and treats anything else — a timeout, a redirect, any non-2xx status — as a failed delivery, and tries again up to three more times with a growing pause (one minute, then longer).

Headers

The body

Every event, including the test delivery, arrives in the same envelope:

Field Type Meaning
event string The event name, for example incident.created.
timestamp string (ISO-8601, UTC) When this attempt was sent. A retry carries a newer time.
deliveryId string Equal to the X-Aegis-Delivery header.
data object The record that changed — its fields per event are listed below.

For example, a new incident:

{
  "event": "incident.created",
  "timestamp": "2026-10-12T09:15:02.311Z",
  "deliveryId": "6f1c2a7e-3b9d-4c55-9e1a-2d8f0b7c4e61",
  "data": {
    "id": "cmg8x1v2k0001qz8l4h3d9f2a",
    "title": "Phishing mail reported by finance",
    "status": "OPEN",
    "severity": "HIGH"
  }
}

The data fields per event

id is the Aegis id of the record; status and severity use the values Aegis shows in the API. Read the record through the API when you need more than these fields. Treat unknown fields as optional — new ones may be added.

Event Fields in data Notes
policy.created id, title, status
policy.updated id, title, status
policy.approved id, title, status title is left out when the approval came from a bulk status change.
policy.acknowledged id, userId, acknowledgedVersion, acknowledgedAt id is the policy, userId the reader. Sent on every acknowledgment, including a re-acknowledgment.
procedure.created id, title, status API only — not offered in the dialog.
procedure.updated id, title, status API only.
procedure.approved id, title, status API only. title is left out after a bulk status change.
risk.created id, title, status
risk.updated id, title, status Not sent when only the status changed.
risk.mitigated id, title, status
vendor.created id, name, status
vendor.assessed id, name, assessmentId, score id is the vendor; assessmentId the new assessment.
evidence.uploaded id, name, type
evidence.expiring id, name, validUntil, daysUntilExpiry Sent once when the item enters the 30-day window and once when it enters the 7-day window.
evidence.expired id, name, validUntil validUntil is an ISO-8601 time, or null.
incident.created id, title, status, severity
incident.resolved id, title, status
compliance.score_changed previousScore, score, totalControls, coveredControls Organisation-wide score, no record id.
control.status_changed id, ref, frameworkKey, previousStatus, status, trigger, actorId trigger is manual, recalculated, cross_framework_sync or workflow; actorId is null when Aegis changed it.
action_item.due_soon entityType, id, title, dueDate, ownerId Sent once, seven days before dueDate. entityType is AuditFinding, NonConformity, GovernanceActionItem or BcmExerciseAction.
action_item.overdue entityType, id, title, dueDate, ownerId Sent once, after dueDate has passed, while the item is still open.
webhook.test message Sent by the test button only; cannot be subscribed to.

Check the signature

Compute the digest over the raw request body exactly as received — not over JSON you parsed and re-serialised — and compare in constant time. In Node.js:

const crypto = require("node:crypto");

function isFromAegis(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Send a test delivery

Test an endpoint before you rely on it, so a wrong URL or a rejected signature turns up at a quiet moment rather than during a real incident. A test runs through the same pipeline as a real event — the address check, the signing and the retries.

  1. Find the endpoint's row. It has to be switched on — the play icon is dimmed and does nothing on a disabled endpoint.
  2. Select the play icon (tooltip: Send test delivery). It turns into a spinner, then Test delivery enqueued. Check delivery history for results. appears. The message carries the event type webhook.test.
  3. Select History on the same row to read the result. The delivery runs in the background, so give it a moment and use the panel's refresh icon if the attempt has not appeared.

Turn an endpoint off, on, or change it

  1. To stop deliveries without losing the configuration, select the green switch on the row. It turns grey, the pill changes to Disabled and Webhook disabled appears. Selecting it again re-enables the endpoint. A disabled endpoint receives nothing and cannot be tested.
  2. To change an endpoint, select the pencil icon. The same dialog opens, now titled Edit Webhook and pre-filled with the current URL, description and ticked events.
  3. Adjust whatever you need. Signing Secret is deliberately blank and shows Leave blank to keep current secret — type a value only to replace the secret, and update the receiving system at the same moment, because messages signed with the new secret start straight away.
  4. Select Update Webhook. The dialog closes, Webhook updated successfully appears and the row refreshes with the new URL, description and event tags.

Read the delivery history

Every attempt against an endpoint is recorded, failures included, so you can see what was sent and how the receiver replied.

  1. Select History on the row (tooltip: View delivery history). A panel opens beneath the row and the button now reads Hide. Once there are attempts to show, the panel is headed Recent Deliveries and lists the ten most recent.
  2. Read each line left to right: a green tick or a red cross, the event type, the HTTP status code the receiver returned (green below 300, red otherwise, N/A when there was no reply), any error text, and the time of the attempt on the right.
  3. Use the refresh icon at the top of the panel to reload it — worth doing straight after a test. If nothing has been delivered the panel reads No delivery history yet. with its own refresh icon beside it. Select Hide to close the panel.

A failed delivery is retried automatically — up to three further attempts, the first about a minute later and with a growing gap after that — and each gets its own line. Several red lines for one event usually mean the receiver is down or rejecting the signature, not that the event was lost.

Delete an endpoint

  1. Select the bin icon on the row. A confirmation dialog opens, titled Delete Webhook, naming the URL you are about to remove.
  2. Read the message: it states that the action cannot be undone and that the endpoint's delivery history goes with it.
  3. Select Delete webhook to go ahead, or Cancel to keep the endpoint. The row disappears and Webhook deleted successfully appears.
Deleting takes the delivery history with it

There is no undo, and the record of what was sent to that endpoint goes too. A message already queued when you delete may still make one last attempt, so a receiver should handle a repeated delivery without harm. To stop messages for a while, disable the endpoint with the switch instead — that keeps both the configuration and its history.

Tips and limits

Where this connects