event_type. All bodies are wrapped in a stable envelope; the
event-specific fields live under payload.
Envelope
| Header | Mirror of |
|---|---|
X-Aegis-Event | event_type |
X-Aegis-Delivery-Id | delivery_id |
X-Aegis-Attempt | attempt |
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).
| Field | Type | Notes |
|---|---|---|
check_id | uuid | Correlates to tx_checks.id; pass to /check-transfer-result/{check_id} for the full body |
tx_hash | string | 32-byte hex with 0x prefix |
chain | string | ETH, BSC, POLYGON, BASE, ARBITRUM, TRON |
subgroup_id | uuid | The subgroup that owns the policy + alert config used |
worst_level | string | Low, Medium, High, Severe — highest level across alerts[] |
alerts | array | Filtered subset above alert_min_level. Each alert mirrors the policy-engine shape, see tx-policy-alert detail |
Alert fields
| Field | Type | Notes |
|---|---|---|
category | string | Display name of the BitOK risk category |
riskLevel | string | Low, Medium, High, Severe |
ruleType | string | One of Direct exposure, Origin of funds / Indirect, Pattern, etc. |
actualShare | number | % of USD-weighted composite that falls in this category |
thresholdShare | number | The rule’s trigger threshold (% share) |
thresholdValue | number | The rule’s trigger threshold (absolute USD) |
entityName | string | null | Top-USD sender label; for indirect rules, prefixed Sender ... (exposed to ...). See sender attribution semantics |
entityAddress | string | null | Anchor sender address — lowercase EVM, base58 TRON |
direction | string | Always incoming in V1 (we only analyse senders, not receivers) |
entityScore | number | 0 by default; reserved for future per-entity sub-scoring |
Synthetic test payload
When fired byPOST /v1/webhooks/{id}/test, the body has the same
shape with an additional top-level synthetic: true and sentinel
ids:
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.
| Field | Type | Notes |
|---|---|---|
job_id | uuid | The exposure_jobs.id of the completed job |
address | string | Anchor address the BFS started from |
network | string | Same chain set as the rest of the API |
report_id | uuid | exposure_reports.id — fetch via GET /explore/{job_id}/result for the full row |
categories | array | USD-weighted contribution per slug, sorted by severity |
complete | boolean | true 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.
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).
| Field | Type | Notes |
|---|---|---|
risk_level | string | high or severe (this event won’t fire below high) |
risk_score | number | 0..100, inverted from category severity |
tier_hit | string | The first tier that escalated the verdict |
categories | array | Slugs of contributing risk categories |

