Decisions Audit
Query the immutable audit trail of every rendered fraud verdict.
Every verdict the engine renders is recorded in fraud_decisions (append-only). The audit endpoints expose that trail for compliance, dispute resolution, and rule-tuning forensics.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/fraud/decisions | List decisions (filterable, paginated). |
| GET | /api/v1/fraud/decisions/:id | Fetch a single decision by id. |
Scope: fraud:audit
Plan gate: Detection Pack addon
Rate limit: 100 requests / hour (intentionally low to deter scraping)
List decisions
GET /api/v1/fraud/decisions
Query parameters
| Parameter | Type | Description |
|---|---|---|
subjectId | string | Filter by the subject's id. |
action | string | Filter by action name. |
resourceKind | string | Filter by resource kind. |
verdict | "allow" | "review" | "step_up" | "block" | Filter by verdict. |
from | ISO-8601 date | Inclusive lower bound on decidedAt. |
to | ISO-8601 date | Inclusive upper bound on decidedAt. |
limit | number (1–100, default 50) | Page size. |
offset | number (default 0) | Pagination offset. |
Response
{
"success": true,
"data": {
"decisions": [
{
"id": "dec_2j7yhq8wcs9...",
"organizationId": "org_acme",
"subjectId": "usr_001",
"subjectKind": "user",
"action": "transfer",
"resourceId": "txn_8a91",
"resourceKind": "transaction",
"verdict": "allow",
"score": 0,
"reasons": [
{ "kind": "default", "code": "phase_6a_default_allow", "weight": 0, "detail": "..." }
],
"context": { "ip": "[REDACTED]", "amount": { "value": 50000, "currency": "NGN" } },
"ruleVersionsEvaluated": null,
"shadow": false,
"latencyMs": 4,
"decidedAt": "2026-05-03T09:14:21.412Z"
}
],
"count": 1,
"limit": 50,
"offset": 0
}
}
Field reference
| Field | Description |
|---|---|
id | Decision id; matches the decisionId returned by decide. |
reasons | The structured chain that produced the verdict. |
context | The non-PII subset of the request context. IPs and user-agents are stored as [REDACTED]; tenants can correlate with their own logs. |
ruleVersionsEvaluated | Which rule versions ran for this decision (populated from Phase 6B onward). |
shadow | true for shadow-decide calls. |
latencyMs | Engine latency only (excludes network). |
decidedAt | When the verdict was rendered. |
Fetch a single decision
GET /api/v1/fraud/decisions/:id
Returns 404 if the decision does not exist or belongs to a different organization. There is no information leakage across tenants.
Response
{
"success": true,
"data": {
"id": "dec_2j7yhq8wcs9...",
"organizationId": "org_acme",
"verdict": "allow",
"...": "..."
}
}
| HTTP | Code | Cause |
|---|---|---|
| 200 | — | Decision returned. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | DETECTION_PACK_REQUIRED | Detection Pack addon not enabled. |
| 403 | FORBIDDEN | API key has no organization context. |
| 404 | NOT_FOUND | Decision id unknown or belongs to a different org. |
| 500 | INTERNAL_ERROR | Unexpected engine failure. |
Retention
Decision rows are retained for 90 days by default. ENTERPRISE-tier organizations can extend retention up to 7 years via contract addendum (see the Compliance Vault pack on the Fraud Detection overview).
The cron/data-retention cron evicts rows past their retention window — eviction is irreversible.
Async write semantics
The fraud_decisions row is written asynchronously by an Inngest durable function after the synchronous decide response is sent. In normal operation rows appear within a few seconds. If you query immediately after decide and miss your row, retry after a short backoff.