Skip to main content
Each event delivery is a POST with a JSON body shaped per event_type. All bodies are wrapped in a stable envelope; the event-specific fields live under payload.

Envelope

{
  "schema_version": 1,
  "event_type":     "tx.policy.alert.triggered",
  "delivery_id":    "11111111-2222-3333-4444-555555555555",
  "attempt":        1,
  "occurred_at":    "2026-05-20T09:14:01.234Z",
  "payload":        { /* event-specific, see below */ }
}
Same envelope mirrored in headers:
HeaderMirror of
X-Aegis-Eventevent_type
X-Aegis-Delivery-Iddelivery_id
X-Aegis-Attemptattempt
delivery_id is stable across retries — use it as your idempotency key.

tx.policy.alert.triggered

Fires from the KYT pipeline when /check-transfer (x402) or /v1/aegis/tx (internal) finds at least one alert above your configured alert_min_level (default Medium).
{
  "check_id":     "1c34db13-bb5a-4f1f-91b6-3c93b40c7f99",
  "tx_hash":      "0x52e8e15b…",
  "chain":        "ETH",
  "subgroup_id":  "00000000-…-system-default",
  "worst_level":  "Severe",
  "alerts": [
    {
      "category":       "Mixer",
      "riskLevel":      "Severe",
      "ruleType":       "Origin of funds / Indirect",
      "actualShare":    52.1,
      "thresholdShare": 1,
      "thresholdValue": 100,
      "entityName":     "Sender 0xab…ef01 (exposed to mixer)",
      "entityAddress":  "0xabcdef…",
      "direction":      "incoming",
      "entityScore":    0
    }
  ]
}
FieldTypeNotes
check_iduuidCorrelates to tx_checks.id; pass to /check-transfer-result/{check_id} for the full body
tx_hashstring32-byte hex with 0x prefix
chainstringETH, BSC, POLYGON, BASE, ARBITRUM, TRON
subgroup_iduuidThe subgroup that owns the policy + alert config used
worst_levelstringLow, Medium, High, Severe — highest level across alerts[]
alertsarrayFiltered subset above alert_min_level. Each alert mirrors the policy-engine shape, see tx-policy-alert detail

Alert fields

FieldTypeNotes
categorystringDisplay name of the BitOK risk category
riskLevelstringLow, Medium, High, Severe
ruleTypestringOne of Direct exposure, Origin of funds / Indirect, Pattern, etc.
actualSharenumber% of USD-weighted composite that falls in this category
thresholdSharenumberThe rule’s trigger threshold (% share)
thresholdValuenumberThe rule’s trigger threshold (absolute USD)
entityNamestring | nullTop-USD sender label; for indirect rules, prefixed Sender ... (exposed to ...). See sender attribution semantics
entityAddressstring | nullAnchor sender address — lowercase EVM, base58 TRON
directionstringAlways incoming in V1 (we only analyse senders, not receivers)
entityScorenumber0 by default; reserved for future per-entity sub-scoring

Synthetic test payload

When fired by POST /v1/webhooks/{id}/test, the body has the same shape with an additional top-level synthetic: true and sentinel ids:
{
  "check_id":     "00000000-0000-0000-0000-000000000000",
  "tx_hash":      "0x0000000000000000000000000000000000000000000000000000000000000000",
  "chain":        "ETH",
  "subgroup_id":  "00000000-0000-0000-0000-000000000000",
  "worst_level":  "Severe",
  "alerts":       [{"category": "Synthetic Test", "riskLevel": "Severe", "ruleType": "Test", "actualShare": 100, "thresholdShare": 0, "direction": "incoming"}],
  "synthetic":    true
}
Receivers MAY short-circuit on synthetic === true to avoid flooding case-review queues with test rows.

aegis.bfs.completed

Fires when an async Tier-4 exposure job (POST /explore) completes successfully. Used for batch / pre-screening flows that can’t tie up a synchronous HTTP request.
{
  "job_id":   "9f0a3a07-37bb-4f99-91b5-…",
  "address":  "0xrecipient…",
  "network":  "ETH",
  "report_id": "f8e9d2…",
  "categories": [
    {"slug": "exchange", "severity": 10, "contribution": 84.3},
    {"slug": "mixer",    "severity": 100, "contribution":  3.1}
  ],
  "complete": true
}
FieldTypeNotes
job_iduuidThe exposure_jobs.id of the completed job
addressstringAnchor address the BFS started from
networkstringSame chain set as the rest of the API
report_iduuidexposure_reports.id — fetch via GET /explore/{job_id}/result for the full row
categoriesarrayUSD-weighted contribution per slug, sorted by severity
completebooleantrue if BFS exhausted within budget, false if budget-capped

aegis.bfs.failed

Same shape minus report_id + categories, plus an error string. Fires after the job exhausts its retry budget.
{
  "job_id":   "9f0a3a07-37bb-…",
  "address":  "0xrecipient…",
  "network":  "ETH",
  "error":    "etherscan rate-limit after 3 retries"
}

aegis.check.high_risk

Fires from /v1/aegis/check when the verdict is risk_level: high or severe (Tier 0/1/2/3/4 — any tier can trigger).
{
  "check_id":   "3b2c1d…",
  "address":    "0xrecipient…",
  "network":    "ETH",
  "risk_level": "high",
  "risk_score": 90,
  "tier_hit":   "tier_2",
  "categories": ["mixer", "darknet_market"]
}
FieldTypeNotes
risk_levelstringhigh or severe (this event won’t fire below high)
risk_scorenumber0..100, inverted from category severity
tier_hitstringThe first tier that escalated the verdict
categoriesarraySlugs of contributing risk categories