Cases
Manual reviewer workflow for flagged decisions — open, triage, escalate, resolve.
Cases give your fraud team a structured workspace to follow up on flagged decisions. Each case ties to one fraud decision (or none, for manual investigations), tracks who's working it, captures notes, runs a 24h default SLA, and ends with one of three resolutions.
Lifecycle
open → triaging → escalated → resolved (terminal)
\ \ ↑
\ → resolved |
→ resolved |
↓
{ confirmed_fraud | false_positive | inconclusive }
| From | To | Notes |
|---|---|---|
open | triaging, escalated, resolved | New cases start here. |
triaging | open, escalated, resolved | A reviewer is actively working it. Reverting to open re-queues. |
escalated | triaging, resolved | SLA breach OR explicit escalation. |
resolved | — | Terminal. To re-investigate, open a new case. |
Auto-open from decide
When /api/v1/fraud/decide returns block, a case is automatically opened with:
decisionId— the originating decisionseverity— equal to the decision score (75–100)- An opening note carrying the triggered reason codes
- 24h default SLA window
This is fire-and-forget: a failure to auto-open never affects the verdict. review and step_up verdicts do not auto-open — subscribe to FRAUD_DECISION_REVIEW (etc.) and call POST /v1/fraud/cases yourself if you want them.
SLA escalation
Cases past their slaBreachAt are bulk-escalated by the fraud-case-sla cron (recommended schedule: every 5 minutes). Each escalation emits FRAUD_CASE_ESCALATED with reason: "sla_breach" so you can wire pager / email / Slack from the workflow engine.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/fraud/cases | List cases (filter by status, decisionId, overdueOnly) |
| POST | /api/v1/fraud/cases | Open a new case |
| GET | /api/v1/fraud/cases/:id | Fetch one case |
| PATCH | /api/v1/fraud/cases/:id | Update severity / assignee / append note |
| POST | /api/v1/fraud/cases/:id/transition | State-machine transition |
| Property | Value |
|---|---|
| Scope | fraud:cases |
| Plan gate | Detection Pack addon |
| Rate limit | 500 requests / hour per API key |
| Idempotent | POST endpoints honour Idempotency-Key |
Open a case
curl -X POST https://api.platformxe.com/api/v1/fraud/cases \
-H "Content-Type: application/json" \
-H "x-api-key: pxk_live_..." \
-d '{
"decisionId": "dec_2j7yhq8wcs9",
"severity": 80,
"assignedTo": "reviewer_jane",
"note": { "by": "reviewer_jane", "text": "Cross-border transfer flagged by velocity rule." }
}'
| Field | Type | Required | Description |
|---|---|---|---|
decisionId | string | null | No | Bind to a fraud decision. null = manual case. |
severity | number (0–100) | No | Defaults to 50. Auto-opened cases carry the decision score. |
assignedTo | string | null | No | Reviewer id / email. |
note | { by, text } | No | Opening note. ≤ 4,000 chars. |
slaWindowMs | number | No | Override the 24h default SLA. |
Returns 201 with the full case record.
Update a case
curl -X PATCH https://api.platformxe.com/api/v1/fraud/cases/fcs_2j7yhq8 \
-H "Content-Type: application/json" \
-H "x-api-key: pxk_live_..." \
-d '{
"severity": 90,
"appendNote": { "by": "reviewer_jane", "text": "Confirmed pattern across 3 accounts." }
}'
PATCH allows updating severity, assignedTo, and appending notes. Existing notes are immutable. Resolved cases are terminal — PATCH returns 409.
Transitions
curl -X POST https://api.platformxe.com/api/v1/fraud/cases/fcs_2j7yhq8/transition \
-H "Content-Type: application/json" \
-H "x-api-key: pxk_live_..." \
-d '{
"target": "resolved",
"resolution": "confirmed_fraud",
"note": { "by": "reviewer_jane", "text": "Account frozen, refund processed." }
}'
Transitioning to resolved requires a resolution:
| Resolution | Use when |
|---|---|
confirmed_fraud | The flagged behaviour was indeed fraud. |
false_positive | Verdict was wrong; no action needed. |
inconclusive | Couldn't determine; closing for triage capacity. |
Tracking the resolution mix is essential for tuning rule thresholds — a false_positive rate trending upward signals a rule is too tight.
List filters
GET /api/v1/fraud/cases accepts:
| Query | Description |
|---|---|
status | open | triaging | escalated | resolved |
decisionId | Cases bound to a specific decision. |
overdueOnly=true | Cases past slaBreachAt. |
limit (≤ 100, default 50) / offset | Pagination. |
Cases come back ordered by openedAt descending so the freshest queue items are at the top.
Events
| Event | When |
|---|---|
FRAUD_CASE_OPENED | New case created (manual or auto-open). |
FRAUD_CASE_ESCALATED | Status moved to escalated. SLA-breach escalations carry reason: "sla_breach". |
FRAUD_CASE_CLOSED | Status moved to resolved. Payload includes the chosen resolution. |
Subscribe to wire your reviewer queue, alert your SOC, or trigger Workflow-engine actions on resolution.
Errors
| HTTP | Code | Cause |
|---|---|---|
| 400 | BAD_REQUEST | Validation failure — invalid severity, missing resolution for resolved, oversized note. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | DETECTION_PACK_REQUIRED | Detection Pack addon not enabled. |
| 403 | FORBIDDEN | API key has no organisation context, or wrong scope. |
| 404 | NOT_FOUND | Case unknown or belongs to a different org. |
| 409 | CONFLICT | PATCH on a resolved case, or invalid state transition. |
| 429 | RATE_LIMITED | 500/hr ceiling exceeded. |
| 500 | INTERNAL_ERROR | Unexpected service failure. |