Skip to main content
Two surfaces, same deliveries:
  • API: https://api.aegis-kyt.com/v1/webhooks/* · X-API-Key: aeg_<48 chars> — for x402 consumers / integrations without a UI session
  • Dashboard: https://app.aegis-kyt.com/webhooks — for VASP / PSP customers using the web app; scoped to your subgroup
Webhooks let your service receive Aegis events as they happen instead of polling. Every delivery is a POST of a JSON body to a URL you register, signed with HMAC-SHA256 using a secret we mint once at subscription creation. Subscriptions registered through either surface receive the same event types with identical payload shapes. See Dashboard self-service for the web UI flow or Manage subscriptions for the REST API flow.

When you’d use webhooks

Event typeFires whenUseful for
tx.policy.alert.triggeredA /check-transfer or /v1/aegis/tx check raises at least one policy alert above your configured alert_min_levelKYT — funnel inbound deposits into your case-review queue
aegis.bfs.completedAn asynchronous Tier-4 exposure job finishesLong-running BFS pre-screening, batch flows
aegis.bfs.failedSame job gives up after retriesSurface the failure into your ops queue
aegis.check.high_riskA /v1/aegis/check returns risk_level high or severeReal-time alerting on standalone address checks
You can subscribe to any non-empty subset; one subscription URL can listen to multiple event types.

Delivery contract

Every delivery is a POST to your URL with these headers:
HeaderValue
Content-Typeapplication/json
User-Agentaegis-webhooks/1
X-Aegis-EventThe event_type string, e.g. tx.policy.alert.triggered
X-Aegis-SignatureBase64-encoded HMAC_SHA256(secret, raw_body)
X-Aegis-Delivery-IdUUID of this delivery attempt — survives retries (idempotency key)
X-Aegis-Attempt1-based attempt counter (1 on first try, 2 on first retry, etc.)
Your endpoint should:
  1. Verify the signature against the raw request body (don’t parse + re-serialize — JSON whitespace matters).
  2. Respond 2xx within 15 s — anything else is retried.
  3. Deduplicate by X-Aegis-Delivery-Id if you store events — we retry on any non-2xx, so the same delivery_id may arrive multiple times during transient outages.

Signature verification

The signature is computed over the exact bytes of the POST body:
import hmac, hashlib, base64

def verify(secret: str, body_bytes: bytes, signature: str) -> bool:
    expected = base64.b64encode(
        hmac.new(secret.encode(), body_bytes, hashlib.sha256).digest()
    ).decode()
    return hmac.compare_digest(expected, signature)
import crypto from "node:crypto";

function verify(secret, bodyBuf, signature) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(bodyBuf)
    .digest("base64");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
Use constant-time comparison (hmac.compare_digest / crypto.timingSafeEqual) — direct === leaks timing.

Retry policy

Delivery attempts on non-2xx response or network error:
AttemptDelay
1immediate
2+30 s
3+2 min
4+10 min
5+1 h
After 5 failed attempts we mark the delivery gave_up. If a subscription accumulates failure_threshold consecutive gave_up deliveries (default 5, configurable per subscription), we auto-disable the subscription and send a Telegram alert to the operator channel. Re-enable via PUT /v1/webhooks/{id} with {is_active: true} — that also resets the failure counter.

Synthetic test event

Use POST /v1/webhooks/{sub_id}/test to send a synthetic event through the same signature + retry plumbing real events use. Payloads carry sentinel ids (00000000-…) and a top-level synthetic: true field so your handler can short-circuit them if desired. See /v1/webhooks//test.

What you don’t get

  • No queueing on your side — if your endpoint is down for >1 h (sum of retry window) we give up on the individual delivery. Critical events should still be polled defensively.
  • No batching — one HTTP call per event, per subscription.
  • No filtering beyond event_type + your subgroup_aml_config alert_min_level — for tx.policy.alert.triggered, see event schemas for the body fields you can route on.

Event schemas

See /webhooks/events for the full body shape of every event type.