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

Alerts

An alert is "something looked off." It's created automatically by screening hits, transaction-evaluate hits, and rule fires — and can be created manually from the dashboard.

Alerts have a short lifecycle. They either get dismissed (false positive), escalated into a case (worth investigating), or marked for SAR filing (clearly suspicious).

Lifecycle

created → open → ┐
                 ├→ resolved      (dismissed by analyst)
                 ├→ escalated     (promoted to case)
                 └→ sar_filed     (case + SAR submitted)

List alerts

GET/api/alerts
Auth · API keyScope · read

Filters:

ParamExampleMeaning
laneonboarding · ongoing · transactionRestrict to one lane
statusopen · escalated · sar_filed · resolvedComma-separated for multi-status
triggerTypescreening_hit · rule_fire · threshold · manualWhat created the alert
customerIdcus_01HXY...Restrict to a single customer
summarytrueAdds aggregate counts to the response
page11-indexed pagination
limit20Default 20, max 1000

Response

{
  "ok": true,
  "data": [
    {
      "id": "alt_01HXY...",
      "customerId": "cus_01HXY...",
      "status": "open",
      "lane": "transaction",
      "triggerType": "rule_fire",
      "riskScore": 65,
      "amountIdr": "550000000",
      "createdAt": "...",
      "customer": { "id": "cus_01HXY...", "fullName": "Umar Patek", "riskRating": "high" },
      "cases": []
    }
  ],
  "total": 47,
  "page": 1,
  "limit": 20,
  "meta": { "pages": 3 },
  "summary": { "open": 12, "escalated": 8, "sarFiled": 3, "resolved": 24 }
}

amountIdr is string because IDR amounts can exceed Number.MAX_SAFE_INTEGER over very large transactions — parse with BigInt().

Read a single alert

GET /api/alerts/{id} returns the full alert with hits[] populated:

{
  "ok": true,
  "data": {
    "id": "alt_01HXY...",
    "customerId": "cus_01HXY...",
    "status": "open",
    "lane": "transaction",
    "triggerType": "rule_fire",
    "riskScore": 65,
    "createdAt": "...",
    "hits": [
      {
        "ruleId": "rul_01HXY...",
        "ruleName": "High Transaction Amount",
        "severity": "medium",
        "scoreContribution": 35
      }
    ],
    "customer": { ... },
    "cases": [],
    "screeningId": null,
    "transactionId": "txn_01HXY..."
  }
}

Dismiss

POST/api/alerts/{id}/dismiss
Auth · API keyScope · alert.write
{
  "reason": "False positive — counterparty is a known business partner with KYC done."
}

Sets status: resolved. The reason field is required (min 20 chars) and audit-logged. Dismissals are reversible by an admin from the dashboard if needed.

Escalate to case

POST/api/alerts/{id}/escalate
Auth · API keyScope · alert.write

If the customer has an open case, the alert is linked to it. Otherwise a new case is created. Response:

{
  "ok": true,
  "data": {
    "alert": { "id": "alt_01HXY...", "status": "escalated" },
    "case":  { "id": "cas_01HXY...", "title": "Case · rule_fire", "status": "open", "newlyCreated": true }
  }
}

You can pass { "caseId": "cas_01HXZ..." } to link to a specific existing case instead of the auto-pick.

Mark for SAR filing

POST/api/alerts/{id}/sar
Auth · API keyScope · sar.write

Doesn't actually file the SAR — that's a separate dashboard workflow. This just bumps status: sar_filed and surfaces the alert in the MLRO's SAR queue.

Listing strategies

Operational dashboards. Use ?summary=true with no filters to power KPI tiles. The summary block returns counts cheaply without scanning all rows.

Analyst inbox. Filter ?status=open&triggerType=screening_hit&page=1&limit=50 for the "new screening hits to review" queue.

Compliance audit. Use cursor pagination via webhook delivery logs rather than scanning /api/alerts?from=...&to=... — list endpoints are not optimized for full-history exports. For full exports, use the dashboard's data export feature.

Don't over-poll

Polling /api/alerts?status=open&since=... every few seconds works at low volume but burns rate limit at scale. Subscribe to alert.created and alert.escalated webhooks instead — see Webhooks.