Event monitoring
Once a user exists as an entity, stream their activity — payments, transfers, logins, profile changes, anything you want scored — to the monitoring engine. Each event is scored against your monitoring rules, stored as a browsable signal, and — when it scores high enough — raises an alert.
Ingest an event
POST /api/identity/events — secret (sk_) key, same base URL and auth as the rest of the Identity Hub.
Request
{
"externalUserId": "user_42",
"externalEventId": "txn_10001",
"type": "transaction.transfer",
"data": {
"amount": 48000,
"currency": "USD",
"country": "NG",
"counterparty": "acct_998877",
"direction": "outbound",
"channel": "transfer",
"ip": "102.89.x.x"
}
}| Field | Required | Notes |
|---|---|---|
externalUserId | Yes | The entity this event belongs to (your own reference). |
externalEventId | Yes | Your stable reference for the event — use it as an idempotency key. |
type | Yes | A free-form event type you choose, e.g. transaction.transfer, login, withdrawal. |
data | No | The event payload the rules read (see below). |
Response
{
"id": "evt_01j9ghi000",
"decision": "ALLOW",
"riskScore": 0,
"alertId": null
}| Field | Meaning |
|---|---|
id | The stored signal's ID. |
decision | ALLOW, REVIEW, or BLOCK — the outcome of scoring against your thresholds. |
riskScore | Composite risk score, 0–100. |
alertId | The alert opened for this event, or null when none was raised. |
Use the decision to gate your own flow — e.g. let ALLOW through, hold REVIEW for manual approval, reject BLOCK.
Scoring rollout. Rule evaluation is being enabled progressively per organization. Until it is active for yours, events are accepted and stored but return
decision: "ALLOW"withriskScore: 0. The request and response shapes do not change when scoring turns on — so you can integrate against this contract now and start acting on decisions the moment it's live.
The data payload
data is a free-form object, but the built-in monitoring rules look for a set of standard fields. Send the ones that apply and the matching rules light up; omit a field and the rules that need it stay dormant.
| Field | Type | Feeds |
|---|---|---|
amount | number | Threshold, Velocity, Pattern (structuring), New-beneficiary, Round-amount, Baseline anomaly. |
currency | string (ISO 4217) | Per-currency money limits across the amount-based rules. |
country | string | Geography, Cross-border. |
counterparty | string | Counterparty lists, New-beneficiary, Smurfing. |
direction | string (inbound/outbound, credit/debit) | Pass-through, Rapid-movement, Smurfing. |
channel | string (e.g. cash, card, crypto) | Cash-intensive. |
ip | string | Available to custom rules. |
Anything else you include is preserved and addressable from custom rules via meta_data.* paths.
The event.flagged webhook
Whenever an event scores to a non-ALLOW decision, a webhook fires so you don't have to poll:
| Event | data |
|---|---|
event.flagged | eventId, decision, riskScore, alertId, externalUserId, type, environment |
Device & session signals
POST /api/identity/signals records device/session signals (fingerprints, session context) tied to an entity. This stream is store-only today — it's persisted for future correlation but not yet scored or surfaced in the dashboard.
{ "externalUserId": "user_42", "type": "device.fingerprint", "data": { "deviceId": "d_123" } }Returns 202 { "id": "sig_01j9jkl111" }.
Where events show up
- Every scored event appears in Dashboard → Compliance → Signals (requires
signals:read). - Events that raise an alert appear in Dashboard → Compliance → Alerts (requires
alerts:read), where your team can group them into a case.
Best practices
- Send one event per logical action, with a stable
externalEventId, so retries don't double-count. - Register the entity first. An event references an entity by
externalUserId; keep your entity records current so rules that readentity.riskTier/entity.nationalityhave data. - Include the standard fields you have — the more of
amount,currency,direction,counterpartyyou send, the more rules can contribute. - Test on sandbox. Use
sk_test_keys and exercise your rule configuration before pointing production traffic at it.
Errors
| Status | Body | Cause |
|---|---|---|
400 | { "error": "invalid_request", "details": … } | Missing externalUserId / externalEventId / type, or malformed body. |
403 | { "error": "secret_key_required" } | A publishable key was used — event ingestion is secret-key only. |
Next steps
- Monitoring rules — tune thresholds and rules that score these events.
- Alerts, cases & SARs — what happens after an event is flagged.