Events
Submits an event for monitoring. In sync mode Sentinel scores it before responding and returns the decision. In async mode, the default, it stores the event and returns straight away.
sync for anything you want decided.Replaying an idempotency_key returns the original event and decision. See Idempotency.
Query parameters
sync or async. Defaults to async.Request body
transfer.outbound, card.authorization, cash_out or login.value is a non-negative number and currency a three-letter ISO 4217 code. Both are required when amount is present.mobile_money, card, bank_transfer, instant_payment or stablecoin. Rules can use it.entity_id and account_id. Counters and alert grouping key on entity_id, so send it on every money event.entity_id when you know who received the money: the money-in counters (fan-in, dormancy, pass-through) and the funds-flow chart only see payments that name a destination entity. Other keys, such as account_id, are stored as sent.id. id is recorded as a uses_device link from the sender, so the alert panel can show other wallets on the same device. Rules can't count devices yet.Sync example
curl -X POST "$SENTINEL_URL/v1/events?mode=sync" \
-H "Authorization: Bearer $SENTINEL_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "transfer.outbound",
"idempotency_key": "txn_8f2a91",
"occurred_at": "2026-10-15T11:02:41Z",
"amount": {
"value": 4950,
"currency": "USD"
},
"rail": "mobile_money",
"source": {
"entity_id": "cus_10442",
"account_id": "wal_8812"
},
"destination": {
"account_id": "wal_6603"
},
"device": {
"id": "dev_a3f19c"
}
}'
Returns 200 with a decision:
{
"id": "5f0c2b7e-3a41-4c1d-9b6e-1f2a3b4c5d6e",
"event_id": "9b21d4c8-7e5f-4a3b-8c2d-6e7f8a9b0c1d",
"decision": "review",
"risk_score": 60,
"alert_id": "c7d4e1a2-5b6c-4d7e-8f9a-0b1c2d3e4f5a",
"decided_in_ms": 38,
"reasons": [
{
"rule": "structuring_below_threshold",
"version": 1,
"detail": "repeated transfers just below the reporting threshold within 24h"
}
],
"created_at": "2026-10-15T11:02:41.041Z"
}
Async example
curl -X POST "$SENTINEL_URL/v1/events" \
-H "Authorization: Bearer $SENTINEL_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "cash_out",
"idempotency_key": "txn_8f2a92",
"occurred_at": "2026-10-15T11:20:05Z",
"amount": {
"value": 9800,
"currency": "USD"
},
"rail": "mobile_money",
"source": {
"entity_id": "cus_10442",
"account_id": "wal_8812"
},
"destination": {
"agent_id": "agt_0442"
}
}'
Returns 202 with the stored event's ID:
{
"id": "e3b0c442-98fc-4c14-9afb-f4c8996fb924",
"received_at": "2026-10-15T11:20:05.208Z"
}
Response fields
Sync (200) returns a decision. See Decisions.
Async (202):
| Field | Type | Meaning |
|---|---|---|
id | string, uuid | Sentinel's ID for the stored event |
received_at | string, date-time | When Sentinel accepted it |
Errors
| Code | error |
|---|---|
400 | type and idempotency_key are required |
400 | amount needs a non-negative value and a 3-letter currency |
401 | missing or invalid API key |
List events
GET /v1/eventsStored events, newest first by occurred_at, each with its decision if it was scored. Lists come a page at a time, newest first. Pass limit (1 to 200, default 50); if there are more, the response has a next_cursor. Send it back as cursor for the next page. Cursors are opaque and keep their place even while new rows arrive.
| Parameter | Meaning |
|---|---|
type | Only this event type |
entity | Events where this entity is the source or the destination |
decision | allow, review or block, or unscored for events stored in async mode |
since, until | Only events with occurred_at after since and up to until |
limit, cursor | Paging, as above |
curl "$SENTINEL_URL/v1/events?decision=review&limit=2" -H "Authorization: Bearer $SENTINEL_KEY"
{
"events": [
{
"id": "c4d5e6f7-0a1b-4c2d-8e9f-3a4b5c6d7e8f",
"idempotency_key": "txn_88123",
"type": "transfer.outbound",
"occurred_at": "2026-10-16T09:12:44Z",
"received_at": "2026-10-16T09:12:44.318Z",
"amount": {
"value": 4900,
"currency": "GHS"
},
"rail": "mobile_money",
"source_id": "wal_8812",
"destination_id": "wal_2290",
"decision": {
"id": "a81e4f02-6c9d-4b7a-9e3f-2d1c0b9a8f7e",
"decision": "review",
"risk_score": 60
}
}
],
"next_cursor": "MTc5MjE2MjQ3NTkxMzAwMDAwMHxjNGQ1ZTZmNw"
}
Get an event
GET /v1/events/{id}One event with the same fields as the list, plus payload (the event exactly as it was sent), reasons from its decision (empty if it wasn't scored or nothing fired) and decided_in_ms. An unknown id is a 404 with event not found.