📘 Public beta · Endpoints are stable; OpenAPI specs and SDKs ship monthly. See changelog →
Products
AML Platform
Cases

Cases

A case is an investigation. It groups one or more alerts, holds analyst notes and attachments, and is the unit at which a SAR (Suspicious Activity Report) is filed.

Lifecycle

open → in_review → ┐
                   ├→ closed_no_action    (resolved benignly)
                   ├→ closed_other        (e.g. customer offboarded)
                   ├→ escalated           (sent to a higher tier — usually MLRO)
                   └→ sar_filed           (terminal — SAR has been submitted)

Terminal states (closed_*, sar_filed) cannot be transitioned out of via the API. Use the dashboard's "reopen" admin action if you need to revisit a case.

Read a case

GET/api/cases/{id}
Auth · API keyScope · read
{
  "ok": true,
  "data": {
    "id": "cas_01HXY...",
    "orgId": "org_01HXY...",
    "customerId": "cus_01HXY...",
    "title": "Case · sanctions_match",
    "status": "in_review",
    "priority": "high",
    "assignedToId": "usr_01HXY...",
    "alertIds": ["alt_01HXY...", "alt_01HXZ..."],
    "notes": [
      {
        "id": "csn_01HXY...",
        "actorUserId": "usr_01HXY...",
        "body": "Counterparty PT Foo also under separate watchlist hit. Possible structuring.",
        "createdAt": "..."
      }
    ],
    "sarFilings": [],
    "createdAt": "...",
    "updatedAt": "..."
  }
}

Update a case

PATCH/api/cases/{id}
Auth · API keyScope · case.write
{
  "status": "in_review",
  "priority": "low | medium | high | critical",
  "assignedToId": "usr_01HXY..." 
}

All fields optional. Pass assignedToId: null to unassign. Terminal-state transitions are rejected with HTTP 409 — close via /close instead.

Add a note

POST/api/cases/{id}/notes
Auth · API keyScope · case.write
{
  "body": "Reviewed source-of-funds documents; customer is a private trader with legitimate IDR 50M+ monthly inflow from confirmed counterparties."
}

Notes are append-only and immutable. The author and timestamp are recorded automatically.

Close a case

POST/api/cases/{id}/close
Auth · API keyScope · case.write
{
  "disposition": "no_action | other",
  "reason": "All linked alerts dismissed as false positives — counterparty verified legitimate."
}

Closing a case auto-resolves all linked alerts that are still in open state. SAR filings on a closed case can still be amended for up to 30 days.

Closing ≠ deleting

Closed cases stay queryable forever. We never delete cases — that would break audit. To "remove" a case from active queues, close it; to fully purge it (data retention compliance), use the dashboard's GDPR/UU-PDP export-and-erase tool, which is per-customer not per-case.

Four-eyes on case actions

By default, the user who creates an alert cannot also close it. Configurable per-org from the decision policy:

PATCH /api/decision-policy
{
  "fourEyesOnCaseClose": true
}

When enabled, attempting to close a case where the closer is also a note author (or the original alert dismissor) returns HTTP 409 four-eyes violation: actor must differ from prior approver.

Promoting an alert directly to a case

You can also create a case independent of an alert via the dashboard. Programmatic creation is via POST /api/alerts/{id}/escalate — see Alerts → Escalate.

Querying cases

The full GET /api/cases list endpoint isn't documented in this MVP because most automation works at the alert level. If you need it, use the dashboard's filterable case queue or the SQL-grade BigQuery export (request via support@quantumelixir.tech).