{
  "info": {
    "_postman_id": "mulisa-screening-api",
    "name": "Mulisa Screening API",
    "description": "# Mulisa Screening API\n\nGenerated from `lib/route-policy.ts` — **110 requests** covering every declared endpoint.\n\nRegenerate with `bun run postman:gen` after adding routes; do not edit this file by hand.\n\n## Auth in one minute\n\n1. Select the **Mulisa — Local** environment.\n2. Screening endpoints (`/api/screen*`, `/api/sanctions/screen`) work immediately with the `apiKey` bearer token.\n3. Everything else needs a console session: run **Auth (console session) / 1. Get CSRF token**, then **2. Sign in**.\n\nEach request's description states its exact auth class and minimum role.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Auth (console session)",
      "description": "Most endpoints require a **console session cookie**, not an API key — only the screening endpoints and `POST /api/approvals/sweep` accept a bearer key.\n\nRun **1. Get CSRF token** then **2. Sign in**. Postman stores the resulting session cookie (`authjs.session-token` over http, `__Secure-authjs.session-token` over https) in its cookie jar and sends it automatically on every later request against the same host.\n\n**The password sign-in below no longer works.** The shared operator password was removed; OIDC is the only way into the console. Sign in through the browser, then copy the `authjs.session-token` cookie into Postman's cookie jar for this host. In local dev with no OIDC provider configured, auth is disabled entirely and every route is open — no sign-in needed.",
      "item": [
        {
          "name": "1. Get CSRF token",
          "request": {
            "method": "GET",
            "header": [],
            "auth": {
              "type": "noauth"
            },
            "url": {
              "raw": "{{baseUrl}}/api/auth/csrf",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "csrf"
              ]
            },
            "description": "Returns `{ \"csrfToken\": \"...\" }` and sets the CSRF cookie. The test script saves the token into the `csrfToken` environment variable for the next request."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "pm.environment.set(\"csrfToken\", body.csrfToken);",
                  "pm.test(\"csrfToken captured\", () => pm.expect(body.csrfToken).to.be.a(\"string\"));"
                ]
              }
            }
          ]
        },
        {
          "name": "2. Sign in (operator password)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/x-www-form-urlencoded",
                "type": "text"
              }
            ],
            "auth": {
              "type": "noauth"
            },
            "url": {
              "raw": "{{baseUrl}}/api/auth/callback/password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "callback",
                "password"
              ]
            },
            "description": "Signs in with the shared operator password (NextAuth credentials provider id `password`). On success Postman receives the `authjs.session-token` cookie. Set `operatorPassword` in the environment first.",
            "body": {
              "mode": "urlencoded",
              "urlencoded": [
                {
                  "key": "csrfToken",
                  "value": "{{csrfToken}}",
                  "type": "text"
                },
                {
                  "key": "password",
                  "value": "{{operatorPassword}}",
                  "type": "text"
                },
                {
                  "key": "callbackUrl",
                  "value": "{{baseUrl}}/dashboard",
                  "type": "text"
                }
              ]
            }
          }
        },
        {
          "name": "3. Current session",
          "request": {
            "method": "GET",
            "header": [],
            "auth": {
              "type": "noauth"
            },
            "url": {
              "raw": "{{baseUrl}}/api/auth/session",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "session"
              ]
            },
            "description": "Verifies the session cookie is live. Returns `{}` when signed out."
          }
        }
      ]
    },
    {
      "name": "Screening",
      "item": [
        {
          "name": "Screen a transaction",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/screen",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nScreens a single transaction and returns an advisory decision in real time. Evaluation order is deterministic: blacklists first (any hit flags at maximum confidence), then active rules whose matched weights sum into risk_score. The transaction is flagged when risk_score reaches your risk_threshold — blocking is always your decision. transaction.id is an instance-wide idempotency key: retries return the original result with HTTP 200 without duplicate cases, statistics, graph updates, or webhooks. Errors use a stable envelope with code, message, request_id, retryable, details, and docs_url.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `transaction` | object | yes | Transaction payload: id, amount, currency, type, channel, timestamp, country_from, country_to, reference?. See the Screening data model guide for the full field catalog. |\n| `sender` | object | yes | Sending party: id, name, account, institution_id, country, kyc_level, risk_score?, pep?. |\n| `receiver` | object | yes | Receiving party: same shape as sender (risk fields optional). |\n| `metadata` | object | no | Optional context: device_id, ip, geo, narrative. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"transaction\": {\n    \"id\": \"txn_20260707_0042\",\n    \"amount\": 1250000,\n    \"currency\": \"KES\",\n    \"type\": \"transfer\",\n    \"channel\": \"api\",\n    \"timestamp\": \"2026-07-07T09:14:00Z\",\n    \"reference\": \"INV-2211\",\n    \"country_from\": \"KE\",\n    \"country_to\": \"NG\"\n  },\n  \"sender\": {\n    \"id\": \"usr_8f2k1\",\n    \"name\": \"Amina Odhiambo\",\n    \"account\": \"0114589022\",\n    \"institution_id\": \"KCBLKENX\",\n    \"country\": \"KE\",\n    \"kyc_level\": \"standard\",\n    \"risk_score\": 0.35,\n    \"pep\": false\n  },\n  \"receiver\": {\n    \"id\": \"usr_m1092\",\n    \"name\": \"Chidi Nwosu\",\n    \"account\": \"3308812304\",\n    \"institution_id\": \"GTBINGLA\",\n    \"country\": \"NG\",\n    \"kyc_level\": \"basic\"\n  },\n  \"metadata\": {\n    \"device_id\": \"dev_a81xz\",\n    \"ip\": \"105.163.2.11\",\n    \"geo\": \"Nairobi\",\n    \"narrative\": \"invoice payment\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Screen a batch of transactions asynchronously",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/screen/batch",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen",
                "batch"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nAccepts up to 1,000 ordinary /api/screen envelopes and answers 202 with a batch_id; each transaction is then screened through the identical pipeline as POST /api/screen, in batch order, by the asynchronous worker. Poll GET /api/screen/batch/{id} for each item's outcome. The batch is accepted whole or not at all: an invalid item or a transaction.id repeated within the batch rejects it with 422 and every offending index in details. transaction.id stays the instance-wide idempotency key, so resubmitting a batch replays rather than rescreens. Each item counts against the rate limit. Returns 503 batch_unavailable on a deployment without the asynchronous tier.\n\n_Policy note: Async batch intake (ROAD-583); each item counts against the rate limit_\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `transactions` | ScreeningRequest[] | yes | 1 to 1,000 screening envelopes, each exactly as POST /api/screen takes them. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"transactions\": [\n    { \"transaction\": { \"id\": \"txn_20260707_0042\", \"amount\": 1250000, \"currency\": \"KES\", \"type\": \"transfer\", \"channel\": \"api\", \"timestamp\": \"2026-07-07T09:14:00Z\", \"country_from\": \"KE\", \"country_to\": \"NG\" },\n      \"sender\": { \"id\": \"usr_8f2k1\", \"name\": \"Amina Odhiambo\", \"account\": \"0114589022\", \"institution_id\": \"KCBLKENX\", \"country\": \"KE\", \"kyc_level\": \"standard\" },\n      \"receiver\": { \"id\": \"usr_3n9q0\", \"name\": \"Chidi Nwosu\", \"account\": \"3308812304\", \"institution_id\": \"GTBINGLA\", \"country\": \"NG\", \"kyc_level\": \"standard\" } }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Read a batch's progress and results",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/screen/batch/:batchId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen",
                "batch",
                ":batchId"
              ],
              "variable": [
                {
                  "key": "batchId",
                  "value": "{{batchId}}",
                  "description": "The batch_id POST /api/screen/batch returned."
                }
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nProgress of a batch accepted by POST /api/screen/batch, and each item's outcome in batch order: status (pending, running, done, failed), attempts, and for a done item its screening_id, decision and risk_score. complete is true once no item is pending or running. A failed item carries the error of its last attempt. Visible only to the environment that submitted it. Batches are deleted 30 days after acceptance; the screenings they produced are kept.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | The batch_id POST /api/screen/batch returned. |"
          }
        },
        {
          "name": "Screen an ISO 20022 pacs.008",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/screen/iso20022",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen",
                "iso20022"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nScreens an ISO 20022 pacs.008 (FIToFICstmrCdtTrf) message. Send the JSON representation of the document — the adapter maps debtor/creditor, agents (BICs), settlement amount, and remittance info to the canonical model, derives country from the agent BIC, then runs the identical pipeline as POST /api/screen. Designed for a scheme/switch to route native clearing messages through screening without reformatting. The response adds an iso20022 block (message_id, end_to_end_id, uetr) and the normalized_request. One CdtTrfTxInf per call.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `Document.FIToFICstmrCdtTrf` | object | yes | The pacs.008 document. The top-level Document envelope is optional. GrpHdr provides MsgId/CreDtTm; CdtTrfTxInf carries PmtId, IntrBkSttlmAmt, Dbtr/DbtrAgt/DbtrAcct, Cdtr/CdtrAgt/CdtrAcct, and RmtInf. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"Document\": {\n    \"FIToFICstmrCdtTrf\": {\n      \"GrpHdr\": { \"MsgId\": \"MSG-20260707-001\", \"CreDtTm\": \"2026-07-07T09:14:00Z\", \"NbOfTxs\": \"1\" },\n      \"CdtTrfTxInf\": {\n        \"PmtId\": { \"EndToEndId\": \"E2E-2211\", \"TxId\": \"txn_20260707_0042\", \"UETR\": \"a1b2c3d4-...\" },\n        \"IntrBkSttlmAmt\": { \"Ccy\": \"KES\", \"value\": \"1250000\" },\n        \"IntrBkSttlmDt\": \"2026-07-07\",\n        \"Dbtr\": { \"Nm\": \"Amina Odhiambo\" },\n        \"DbtrAcct\": { \"Id\": { \"Othr\": { \"Id\": \"0114589022\" } } },\n        \"DbtrAgt\": { \"FinInstnId\": { \"BICFI\": \"KCBLKENX\" } },\n        \"Cdtr\": { \"Nm\": \"Chidi Nwosu\" },\n        \"CdtrAcct\": { \"Id\": { \"Othr\": { \"Id\": \"3308812304\" } } },\n        \"CdtrAgt\": { \"FinInstnId\": { \"BICFI\": \"GTBINGLA\" } },\n        \"RmtInf\": { \"Ustrd\": [\"invoice payment\"] }\n      }\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Screen an ISO 8583 message",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/screen/iso8583",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen",
                "iso8583"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nScreens an ISO 8583 financial transaction message (MTI 0100/0200). The adapter maps DE2 (PAN), DE4 (amount), DE103 (commission) and other data elements to the canonical model, then runs the identical screening pipeline. Designed for card acquirers and switches routing native ISO 8583 traffic through screening. The response adds an iso8583 block with the adapted request and any parsing warnings.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `mti` | string | yes | 4-digit message type indicator (e.g. \"0100\", \"0200\"). |\n| `fields` | object | yes | Bitmap-indexed data elements keyed by field number (e.g. \"2\": \"...\" for PAN). |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"mti\": \"0200\",\n  \"fields\": {\n    \"2\": \"4111111111111111\",\n    \"3\": \"000000\",\n    \"4\": \"000125000000\",\n    \"7\": \"0707091400\",\n    \"11\": \"004212\",\n    \"18\": \"4829\",\n    \"22\": \"051\",\n    \"32\": \"KCBLKENX\",\n    \"37\": \"707000004212\",\n    \"41\": \"TERM0001\",\n    \"42\": \"MERCH00012345\",\n    \"43\": \"MULISA TEST MERCHANT   NAIROBI      KE\",\n    \"49\": \"404\",\n    \"102\": \"0114589022\",\n    \"103\": \"3308812304\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Screen a Mojaloop transaction",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/screen/mojaloop",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "screen",
                "mojaloop"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nScreens a Mojaloop transaction object (the payload exchanged during the quote/transfer flow). The adapter maps payer/payee partyIdInfo to the canonical model — fspId becomes institution_id (the emitting FSP) and MSISDN identifiers infer the country from the calling code — then runs the identical pipeline as POST /api/screen. Designed for a Mojaloop hub to screen inline. The response adds a mojaloop block (transaction_id, quote_id) and the normalized_request.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `transactionId` | string | yes | Mojaloop transaction identifier. |\n| `amount` | object | yes | { amount, currency } — e.g. { \"amount\": \"1250000\", \"currency\": \"KES\" }. |\n| `payer` | object | yes | Payer party: { partyIdInfo: { partyIdType, partyIdentifier, fspId }, name?, personalInfo? }. |\n| `payee` | object | yes | Payee party: same shape as payer. |\n| `transactionType` | object | no | { scenario, initiator, initiatorType } — scenario maps to the canonical transaction type. |\n| `note` | string | no | Free-text note, mapped to metadata.narrative. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"transactionId\": \"b51ec534-ee48-4575-b6a9-ead2955b8069\",\n  \"quoteId\": \"7c23e6c0-1a2b-4f3d-9e8a-2b1c0d4e5f6a\",\n  \"amount\": { \"amount\": \"1250000\", \"currency\": \"KES\" },\n  \"transactionType\": { \"scenario\": \"TRANSFER\", \"initiator\": \"PAYER\", \"initiatorType\": \"CONSUMER\" },\n  \"payer\": {\n    \"partyIdInfo\": { \"partyIdType\": \"MSISDN\", \"partyIdentifier\": \"254712345678\", \"fspId\": \"umojafsp\" },\n    \"name\": \"Amina Odhiambo\"\n  },\n  \"payee\": {\n    \"partyIdInfo\": { \"partyIdType\": \"MSISDN\", \"partyIdentifier\": \"233201234567\", \"fspId\": \"gtbfsp\" },\n    \"name\": \"Chidi Nwosu\"\n  },\n  \"note\": \"invoice payment\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Transactions",
      "item": [
        {
          "name": "List screened transactions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/transactions?status=&type=&channel=&currency=&country=&reviewed=&disposition=&q=&date_from=&date_to=&amount_min=&amount_max=&risk_min=&limit=&offset=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transactions"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "disabled": true,
                  "description": "cleared | flagged"
                },
                {
                  "key": "type",
                  "value": "",
                  "disabled": true,
                  "description": "Transaction type filter (transfer, card, cash_deposit, remittance, mobile_money)."
                },
                {
                  "key": "channel",
                  "value": "",
                  "disabled": true,
                  "description": "api | mobile | branch | agent"
                },
                {
                  "key": "currency",
                  "value": "",
                  "disabled": true,
                  "description": "Currency code filter, e.g. KES."
                },
                {
                  "key": "country",
                  "value": "",
                  "disabled": true,
                  "description": "Corridor filter — matches country_from OR country_to."
                },
                {
                  "key": "reviewed",
                  "value": "",
                  "disabled": true,
                  "description": "true | false"
                },
                {
                  "key": "disposition",
                  "value": "",
                  "disabled": true,
                  "description": "true_positive | false_positive | escalated"
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true,
                  "description": "Full-text search on id, names, and accounts."
                },
                {
                  "key": "date_from",
                  "value": "",
                  "disabled": true,
                  "description": "Inclusive lower bound on transaction timestamp (ISO date)."
                },
                {
                  "key": "date_to",
                  "value": "",
                  "disabled": true,
                  "description": "Inclusive upper bound; date-only values mean end of day."
                },
                {
                  "key": "amount_min",
                  "value": "",
                  "disabled": true,
                  "description": "Minimum transaction amount."
                },
                {
                  "key": "amount_max",
                  "value": "",
                  "disabled": true,
                  "description": "Maximum transaction amount."
                },
                {
                  "key": "risk_min",
                  "value": "",
                  "disabled": true,
                  "description": "Minimum risk_score (0..1)."
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": "Page size, max 500 (default 25)."
                },
                {
                  "key": "offset",
                  "value": "",
                  "disabled": true,
                  "description": "Zero-based pagination offset."
                }
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nLists screened transactions from reporting history, newest first, with pagination. The response includes total, limit, offset, and a counts object ({ all, cleared, flagged }) computed before the status filter so status tabs stay accurate for the active filter set.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `status` | enum | no | cleared | flagged |\n| `type` | string | no | Transaction type filter (transfer, card, cash_deposit, remittance, mobile_money). |\n| `channel` | enum | no | api | mobile | branch | agent |\n| `currency` | string | no | Currency code filter, e.g. KES. |\n| `country` | string | no | Corridor filter — matches country_from OR country_to. |\n| `reviewed` | boolean | no | true | false |\n| `disposition` | enum | no | true_positive | false_positive | escalated |\n| `q` | string | no | Full-text search on id, names, and accounts. |\n| `date_from` | date | no | Inclusive lower bound on transaction timestamp (ISO date). |\n| `date_to` | date | no | Inclusive upper bound; date-only values mean end of day. |\n| `amount_min` | number | no | Minimum transaction amount. |\n| `amount_max` | number | no | Maximum transaction amount. |\n| `risk_min` | number | no | Minimum risk_score (0..1). |\n| `limit` | number | no | Page size, max 500 (default 25). |\n| `offset` | number | no | Zero-based pagination offset. |"
          }
        },
        {
          "name": "Review / disposition a record",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transactions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transactions"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nMarks a screening record as reviewed and/or sets the analyst disposition. Dispositions are retained as review labels; model-assisted scoring is coming soon. Escalated dispositions generate SAR drafts.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `screening_id` | string | yes | The screening decision to update. |\n| `reviewed` | boolean | no | Mark the record reviewed. |\n| `disposition` | enum | no | true_positive | false_positive | escalated |\n| `note` | string | no | Optional analyst note, included in SAR drafts. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"screening_id\": \"scr_x81ka02f\",\n  \"reviewed\": true,\n  \"disposition\": \"escalated\",\n  \"note\": \"Structuring pattern across 3 counterparties.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Bulk-import transactions",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/transactions/import",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "transactions",
                "import"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nBulk-screens a CSV/JSON batch into the transaction history (unlike the sandbox, results are persisted). dry_run: true validates without screening. Idempotent: duplicate transaction ids are skipped. Requires analyst role.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"raw\": \"string\",\n  \"filename\": \"string\",\n  \"dry_run\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Cases",
      "item": [
        {
          "name": "List cases",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/cases?status=&priority=&assignee=&institution=&sla=&q=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "cases"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "disabled": true,
                  "description": "open | investigating | escalated | closed"
                },
                {
                  "key": "priority",
                  "value": "",
                  "disabled": true,
                  "description": "low | medium | high | critical"
                },
                {
                  "key": "assignee",
                  "value": "",
                  "disabled": true,
                  "description": "Analyst id, or \"unassigned\"."
                },
                {
                  "key": "institution",
                  "value": "",
                  "disabled": true,
                  "description": "Emitting FSP (institution_id) of the case subject."
                },
                {
                  "key": "sla",
                  "value": "",
                  "disabled": true,
                  "description": "breached — only cases past their SLA due time."
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true,
                  "description": "Search on case id, subject name, or subject account."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists investigation cases, newest activity first, with queue stats and the distinct set of emitting institutions (the FSP triage dimension for switch / central-bank deployments). Non-cleared screening decisions automatically open or attach to a case per subject.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `status` | enum | no | open | investigating | escalated | closed |\n| `priority` | enum | no | low | medium | high | critical |\n| `assignee` | string | no | Analyst id, or \"unassigned\". |\n| `institution` | string | no | Emitting FSP (institution_id) of the case subject. |\n| `sla` | enum | no | breached — only cases past their SLA due time. |\n| `q` | string | no | Search on case id, subject name, or subject account. |"
          }
        },
        {
          "name": "Open a case manually",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/cases",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "cases"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nOpens a case manually — e.g. from an external referral, regulator request, or offline investigation. System-generated cases are created automatically by the screening pipeline; use this only for cases that did not originate from an alert.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `title` | string | yes | Human-readable case title. |\n| `subject` | object | yes | Case subject. Requires subject.account; optional name, institution_id, country, role (sender | receiver). |\n| `priority` | enum | no | low | medium | high | critical (default medium). Sets the SLA due time. |\n| `note` | string | no | Optional opening note added to the timeline. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"Regulator referral — suspected mule ring\",\n  \"priority\": \"high\",\n  \"subject\": {\n    \"account\": \"0114589022\",\n    \"name\": \"Amina Odhiambo\",\n    \"institution_id\": \"KCBLKENX\",\n    \"country\": \"KE\",\n    \"role\": \"receiver\"\n  },\n  \"note\": \"Referred by UIF on 2026-07-08.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Fetch a case",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/cases/:caseId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "cases",
                ":caseId"
              ],
              "variable": [
                {
                  "key": "caseId",
                  "value": "{{caseId}}",
                  "description": "Case id (e.g. case_20260705_0015)."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFetches a single case with its full audit timeline plus the complete screening records of every linked alert, so an investigator sees the underlying transactions, rule hits, and typology breaches in one call.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Case id (e.g. case_20260705_0015). |"
          }
        },
        {
          "name": "Act on a case",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/cases/:caseId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "cases",
                ":caseId"
              ],
              "variable": [
                {
                  "key": "caseId",
                  "value": "{{caseId}}",
                  "description": "Case id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nPerforms an analyst action. Status transitions are validated (open→investigating/closed, investigating→escalated/closed, escalated→closed/investigating, closed→open to reopen). Priority changes re-derive the SLA. Every action appends an immutable event to the case timeline (the audit trail).\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Case id. |\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `status` | enum | no | open | investigating | escalated | closed (validated transition). |\n| `assignee` | string | no | Assign / reassign to an analyst; empty string unassigns. |\n| `priority` | enum | no | low | medium | high | critical — re-derives the SLA due time. |\n| `note` | string | no | Append an investigation note. |\n| `resolution` | enum | no | confirmed_suspicious | false_positive | reported | no_action — closes the case. |\n| `resolution_note` | string | no | Optional rationale recorded with the resolution. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"resolution\": \"reported\",\n  \"resolution_note\": \"STR filed with UIF, ref 2026-0442.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Rules",
      "item": [
        {
          "name": "List rules",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/rules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists all rules with status, version, and live performance stats (trigger_count, hit_rate — updated on every screening)."
          }
        },
        {
          "name": "Create a rule",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/rules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nCreates a rule from typed YAML. The YAML is validated against the field catalog and operator set before acceptance. See the Rule YAML guide for the schema.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `yaml` | string | yes | Rule definition in typed YAML. |\n| `status` | enum | no | draft (default) | active | disabled |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"yaml\": \"rule:\\n  id: example-high-value-remittance\\n  name: Example high-value remittance\\n  severity: medium\\n  weight: 0.5\\n  when:\\n    all:\\n      - field: transaction.amount\\n        op: gt\\n        value: 500000\",\n  \"status\": \"draft\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Fetch a rule",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/rules/:ruleId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules",
                ":ruleId"
              ],
              "variable": [
                {
                  "key": "ruleId",
                  "value": "{{ruleId}}",
                  "description": "Rule id (matches the id in the YAML)."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFetches a single rule by id, including its YAML source and stats.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Rule id (matches the id in the YAML). |"
          }
        },
        {
          "name": "Update a rule",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/rules/:ruleId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules",
                ":ruleId"
              ],
              "variable": [
                {
                  "key": "ruleId",
                  "value": "{{ruleId}}",
                  "description": "Rule id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUpdates rule YAML and/or status. The rule id inside the YAML must match the path id — create a new rule to change the id. YAML updates increment the rule version.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Rule id. |\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `yaml` | string | no | New YAML (validated; bumps version). |\n| `status` | enum | no | active | draft | disabled |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"yaml\": \"rule:\\n  id: {{ruleId}}\\n  name: Updated rule\\n  severity: high\\n  weight: 0.75\\n  when:\\n    all:\\n      - field: transaction.amount\\n        op: gt\\n        value: 2000000\",\n  \"status\": \"active\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Delete a rule",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/rules/:ruleId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules",
                ":ruleId"
              ],
              "variable": [
                {
                  "key": "ruleId",
                  "value": "{{ruleId}}",
                  "description": "Rule id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nPermanently deletes a rule.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Rule id. |"
          }
        },
        {
          "name": "List rule versions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/rules/:ruleId/versions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules",
                ":ruleId",
                "versions"
              ],
              "variable": [
                {
                  "key": "ruleId",
                  "value": "{{ruleId}}",
                  "description": "Rule id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the full version history for a rule, newest first. Each entry records the YAML snapshot, version number, status at that version, and who made the change.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Rule id. |"
          }
        },
        {
          "name": "Bulk create / upsert rules",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/rules/bulk",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "rules",
                "bulk"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nCreates or upserts many rules in one request — the surface behind the console’s bulk import and infra-as-code tooling like the Terraform provider (which renders a rule set per environment and posts it here). Accepts a multi-document YAML string (documents separated by \"---\") or a \"rules\" array. Each rule is validated independently and reported with its own outcome (created / replaced / skipped / error). Use on_conflict=replace for idempotent syncs.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `yaml` | string | no | Multiple rule documents separated by \"---\". Provide this OR \"rules\". |\n| `rules` | array | no | Array of rule YAML strings, or objects { yaml, status }. Provide this OR \"yaml\". |\n| `on_conflict` | enum | no | skip (default) keeps existing; replace upserts (idempotent); error fails conflicting items. |\n| `atomic` | boolean | no | When true, validate everything first and apply nothing if any item errors. Default false. |\n| `default_status` | enum | no | draft (default) | active — status for items that don’t set their own. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"on_conflict\": \"replace\",\n  \"default_status\": \"active\",\n  \"yaml\": \"rule:\\n  id: high-value-cross-border\\n  name: High-value cross-border\\n  severity: high\\n  weight: 0.7\\n  when:\\n    all:\\n      - field: transaction.amount\\n        op: gt\\n        value: 1000000\\n---\\nrule:\\n  id: dormant-account-burst\\n  name: Dormant account burst\\n  severity: medium\\n  weight: 0.5\\n  when:\\n    all:\\n      - field: profile.new_counterparty\\n        op: eq\\n        value: true\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Typologies",
      "item": [
        {
          "name": "List typologies",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/typologies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists all typologies with status, version, and live trigger stats."
          }
        },
        {
          "name": "Create a typology",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/typologies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nCreates a typology from typed YAML. The YAML is validated for structure, weights, and thresholds, and every referenced rule_id must already exist.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `yaml` | string | yes | Typology definition in typed YAML. |\n| `status` | enum | no | draft (default) | active |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"yaml\": \"typology:\\n  id: example-cross-border-burst\\n  name: Example cross-border burst\\n  category: layering\\n  rules:\\n    - rule_id: high-value-cross-border\\n      weight: 0.6\\n    - rule_id: dormant-account-burst\\n      weight: 0.4\\n  alert_threshold: 0.6\\n  interdict_threshold: 0.9\",\n  \"status\": \"active\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Fetch a typology",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/typologies/:typologyId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies",
                ":typologyId"
              ],
              "variable": [
                {
                  "key": "typologyId",
                  "value": "{{typologyId}}",
                  "description": "Typology id (matches the id in the YAML)."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFetches a single typology by id, including its YAML source, member rules, thresholds, and stats.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Typology id (matches the id in the YAML). |"
          }
        },
        {
          "name": "Update a typology",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/typologies/:typologyId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies",
                ":typologyId"
              ],
              "variable": [
                {
                  "key": "typologyId",
                  "value": "{{typologyId}}",
                  "description": "Typology id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUpdates typology YAML and/or status. The id inside the YAML must match the path id — create a new typology to change the id. Referenced rules must exist; YAML updates increment the version.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Typology id. |\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `yaml` | string | no | New YAML (validated; bumps version). |\n| `status` | enum | no | active | draft | disabled |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"yaml\": \"typology:\\n  id: {{typologyId}}\\n  name: Updated typology\\n  category: layering\\n  rules:\\n    - rule_id: high-value-cross-border\\n      weight: 0.7\\n    - rule_id: dormant-account-burst\\n      weight: 0.3\\n  alert_threshold: 0.65\\n  interdict_threshold: 0.9\",\n  \"status\": \"active\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Delete a typology",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/typologies/:typologyId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies",
                ":typologyId"
              ],
              "variable": [
                {
                  "key": "typologyId",
                  "value": "{{typologyId}}",
                  "description": "Typology id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nPermanently deletes a typology. Member rules are unaffected.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Typology id. |"
          }
        },
        {
          "name": "List typology versions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/typologies/:typologyId/versions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "typologies",
                ":typologyId",
                "versions"
              ],
              "variable": [
                {
                  "key": "typologyId",
                  "value": "{{typologyId}}",
                  "description": "Typology id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the full version history for a typology, newest first. Each entry records the YAML snapshot, version number, status at that version, and who made the change.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Typology id. |"
          }
        }
      ]
    },
    {
      "name": "Sandbox",
      "item": [
        {
          "name": "Run a sandbox simulation",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/sandbox/run",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sandbox",
                "run"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRuns transactions through the full engine without persisting anything to reporting history.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `transactions` | array | yes | ScreeningRequest[] to evaluate. |\n| `rule_ids` | string[] | no | Restrict evaluation to specific rules (defaults to all active). |\n| `include_drafts` | boolean | no | Also evaluate draft rules. |\n| `yaml` | string | no | Ad-hoc rule YAML to test without saving. |\n| `yaml_only` | boolean | no | Evaluate only the ad-hoc YAML rule. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"transactions\": [],\n  \"rule_ids\": \"\",\n  \"include_drafts\": false,\n  \"yaml\": \"\",\n  \"yaml_only\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Get sample transactions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sandbox/sample",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sandbox",
                "sample"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns a ready-made set of representative sample transactions for use as sandbox input."
          }
        },
        {
          "name": "Get test scenarios",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sandbox/scenarios",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sandbox",
                "scenarios"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns YAML test scenarios — predefined rule + typology configurations with matching transaction samples for manual or automated testing."
          }
        }
      ]
    },
    {
      "name": "Blacklists",
      "item": [
        {
          "name": "List blacklist entries",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/blacklists?list=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "blacklists"
              ],
              "query": [
                {
                  "key": "list",
                  "value": "",
                  "disabled": true,
                  "description": "accounts | institutions | corridors | devices | transaction_types"
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists blacklist entries. Expired entries (past expires_at) remain listed but are skipped during screening.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `list` | enum | no | accounts | institutions | corridors | devices | transaction_types |"
          }
        },
        {
          "name": "Add a blacklist entry",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/blacklists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "blacklists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nAdds an entry. Any screening hit on a non-expired entry results in status \"flagged\" with maximum confidence, regardless of rules or model scores.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `list` | enum | yes | accounts | institutions | corridors | devices | transaction_types |\n| `value` | string | yes | The value to match (account number, BIC, \"KE->NG\" corridor, ...). |\n| `reason` | string | no | Audit note. |\n| `expires_at` | datetime | no | Optional expiry; entry is ignored after this time. |\n| `min_amount` | number | no | Only match at or above this amount. |\n| `corridor` | string | no | Restrict the entry to a corridor. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"list\": \"accounts\",\n  \"value\": \"0114589022\",\n  \"reason\": \"Confirmed mule account — regulator referral.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Remove an entry",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/blacklists?id={{blacklistId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "blacklists"
              ],
              "query": [
                {
                  "key": "id",
                  "value": "{{blacklistId}}",
                  "description": "Entry id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRemoves a blacklist entry by id.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Entry id. |"
          }
        }
      ]
    },
    {
      "name": "Profiles",
      "item": [
        {
          "name": "List behavioral profiles",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles?risk_band=&q=&limit=&offset=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles"
              ],
              "query": [
                {
                  "key": "risk_band",
                  "value": "",
                  "disabled": true,
                  "description": "low | medium | high"
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true,
                  "description": "Search on subject."
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": "Page size."
                },
                {
                  "key": "offset",
                  "value": "",
                  "disabled": true,
                  "description": "Pagination offset."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists behavioral profiles built from screening history. subject_id is the sender account number. A profile is created from the subject’s first screened transaction and matures (emerging → established → mature) as history accrues; the confidence field reflects how much history backs it.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `risk_band` | enum | no | low | medium | high |\n| `q` | string | no | Search on subject. |\n| `limit` | number | no | Page size. |\n| `offset` | number | no | Pagination offset. |"
          }
        },
        {
          "name": "Fetch a profile",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/:profileId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                ":profileId"
              ],
              "variable": [
                {
                  "key": "profileId",
                  "value": "{{profileId}}",
                  "description": "Sender account number."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFull profile: derived features (typical amounts, hours, channels, corridors), risk band, embedding metadata, and recent_transactions[].\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `subject_id` | string | yes | Sender account number. |"
          }
        },
        {
          "name": "Peer group",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/:profileId/neighbors?k=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                ":profileId",
                "neighbors"
              ],
              "query": [
                {
                  "key": "k",
                  "value": "",
                  "disabled": true,
                  "description": "Neighbors to return (default 5, max 20)."
                }
              ],
              "variable": [
                {
                  "key": "profileId",
                  "value": "{{profileId}}",
                  "description": "Sender account number."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nProfiles most similar in embedding space — the subject’s behavioral peer group.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `subject_id` | string | yes | Sender account number. |\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `k` | number | no | Neighbors to return (default 5, max 20). |"
          }
        },
        {
          "name": "Counterparty network",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/:profileId/network?depth=&min_txn=&flagged_only=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                ":profileId",
                "network"
              ],
              "query": [
                {
                  "key": "depth",
                  "value": "",
                  "disabled": true,
                  "description": "Traversal depth, 1–6 (default 1). Node cap 300."
                },
                {
                  "key": "min_txn",
                  "value": "",
                  "disabled": true,
                  "description": "Minimum transactions per edge."
                },
                {
                  "key": "flagged_only",
                  "value": "",
                  "disabled": true,
                  "description": "Only include flagged edges."
                }
              ],
              "variable": [
                {
                  "key": "profileId",
                  "value": "{{profileId}}",
                  "description": "Sender account number."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nCounterparty network graph served from the incrementally-maintained graph index (updated O(1) per screening — no history rescans). Returns { network: { root, nodes[], edges[], truncated } }. Nodes carry depth, risk band, and blacklist status; edges carry txn_count, total_amount, flagged_count, corridors, and first/last seen.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `subject_id` | string | yes | Sender account number. |\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `depth` | number | no | Traversal depth, 1–6 (default 1). Node cap 300. |\n| `min_txn` | number | no | Minimum transactions per edge. |\n| `flagged_only` | boolean | no | Only include flagged edges. |"
          }
        },
        {
          "name": "What-if scoring",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/:profileId/score",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                ":profileId",
                "score"
              ],
              "variable": [
                {
                  "key": "profileId",
                  "value": "{{profileId}}",
                  "description": "Sender account number."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nScores a candidate transaction against the profile without screening it. Returns anomaly_score and per-feature deviations — useful for pre-flight checks and tuning profile-aware rules.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `subject_id` | string | yes | Sender account number. |\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `transaction` | object | yes | { amount, type?, channel?, country_to?, country_from?, timestamp? } |\n| `receiver_account` | string | no | Candidate counterparty for new-counterparty detection. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"transaction\": {\n    \"amount\": 1250000,\n    \"type\": \"transfer\",\n    \"channel\": \"api\",\n    \"country_from\": \"KE\",\n    \"country_to\": \"NG\",\n    \"timestamp\": \"2026-07-07T09:14:00Z\"\n  },\n  \"receiver_account\": \"3308812304\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Follow-the-money trace",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/:profileId/trace?direction=&max_hops=&max_paths=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                ":profileId",
                "trace"
              ],
              "query": [
                {
                  "key": "direction",
                  "value": "",
                  "disabled": true,
                  "description": "outbound | inbound (default outbound)."
                },
                {
                  "key": "max_hops",
                  "value": "",
                  "disabled": true,
                  "description": "Maximum path depth, 1–6 (default 4)."
                },
                {
                  "key": "max_paths",
                  "value": "",
                  "disabled": true,
                  "description": "Maximum paths returned (default 25)."
                }
              ],
              "variable": [
                {
                  "key": "profileId",
                  "value": "{{profileId}}",
                  "description": "Sender account number."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nDirected, time-aware path search over the pre-aggregated edge graph — answers \"where did the money go\" (outbound) or \"where did it come from\" (inbound). Money in must precede money out at each hop. Returns { trace: { root, direction, paths[], truncated } }; each path carries ordered hops, bottleneck volume, per-hop attenuation, and a suspicious flag when any edge on the path has flagged activity. Paths are ranked by volume.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `subject_id` | string | yes | Sender account number. |\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `direction` | enum | no | outbound | inbound (default outbound). |\n| `max_hops` | number | no | Maximum path depth, 1–6 (default 4). |\n| `max_paths` | number | no | Maximum paths returned (default 25). |"
          }
        },
        {
          "name": "Rebuild profiles",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/build",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                "build"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nTriggers an async profile (re)build over screening history. Returns a build job (202 Accepted) — poll the status endpoint."
          }
        },
        {
          "name": "Poll build job",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/build/:jobId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                "build",
                ":jobId"
              ],
              "variable": [
                {
                  "key": "jobId",
                  "value": "{{jobId}}",
                  "description": "Job id from POST /profiles/build."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nPolls the status of a profile build job. Returns { job: { job_id, status, subjects_processed, subjects_total, ... } }.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `job_id` | string | yes | Job id from POST /profiles/build. |"
          }
        },
        {
          "name": "List entity alias candidates",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/resolve",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                "resolve"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns entity alias candidates for identity resolution — accounts that may belong to the same entity. Surface pending candidates for analyst review."
          }
        },
        {
          "name": "Review an alias candidate",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/profiles/resolve",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "profiles",
                "resolve"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nApproves or rejects an entity alias candidate. Approved aliases merge the profiles; rejected candidates are dismissed.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"{{aliasId}}\",\n  \"status\": \"approved\",\n  \"note\": \"Same customer — matching name and shared device.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Networks",
      "item": [
        {
          "name": "List extracted networks",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/networks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "networks"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFull-graph network extraction: every connected community in the transaction graph, scored as a unit. Returns { overview: { networks[], patterns[], links[], node_count, edge_count } }. Each network carries a composite risk_score (flag rate, blacklist members, high-risk profiles, detected patterns), member/volume stats, and top accounts. patterns[] are structural motifs (fan_in, fan_out, pass_through, cycle) with strength scores; links[] connect networks via shared institutions or name-based identity matches. Recomputed only when new transactions land — cached per graph-index version."
          }
        },
        {
          "name": "Fetch an extracted network",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/networks/:networkId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "networks",
                ":networkId"
              ],
              "variable": [
                {
                  "key": "networkId",
                  "value": "{{networkId}}",
                  "description": "Network id (e.g. net_0111797308) from GET /api/networks."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nOne extracted network in full: summary stats, the renderable graph ({ nodes[], edges[] } — same shape as the counterparty network), the structural patterns detected inside it, and its cross-network links. 404 if the network id no longer exists (networks are re-extracted as the graph evolves).\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Network id (e.g. net_0111797308) from GET /api/networks. |"
          }
        }
      ]
    },
    {
      "name": "Webhooks",
      "item": [
        {
          "name": "List webhooks",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists registered endpoints (secrets redacted), the complete event catalog, and the 100 most recent delivery records."
          }
        },
        {
          "name": "Register a webhook",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRegisters a public HTTPS endpoint for versioned AML events. The HMAC secret is returned once. Signatures use t=<unix>,v1=<hex> over timestamp.raw_body. Simulate mode sends real signed test requests with the same timeout, retry, dead-letter, and replay behavior as live delivery.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `url` | string | yes | Destination URL. HTTPS is required for live transport. |\n| `events` | string[] | yes | Exact event names, group wildcards such as screening.*, or *. |\n| `transport` | string | no | https (default) | simulate |\n| `description` | string | no | Human-readable endpoint purpose. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://core.yourbank.example/aml/webhook\",\n  \"events\": [\"screening.*\", \"case.status_changed\", \"sar.submitted\"],\n  \"transport\": \"https\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Update a webhook",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUpdates the URL, subscriptions, transport, description, or active state of an endpoint.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Webhook id. |\n| `events` | string[] | no | Replacement subscription list. |\n| `active` | boolean | no | Enable or pause delivery. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"string\",\n  \"url\": \"string\",\n  \"events\": [],\n  \"transport\": \"https\",\n  \"active\": false,\n  \"description\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Delete a webhook",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks?id={{webhookId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks"
              ],
              "query": [
                {
                  "key": "id",
                  "value": "{{webhookId}}",
                  "description": "Webhook id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRemoves a webhook endpoint. In-flight deliveries are not cancelled.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Webhook id. |"
          }
        },
        {
          "name": "Test the failure-notification destination",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks/alert-test",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks",
                "alert-test"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nSends a synthetic delivery-failure notification to the destination configured in organization settings (webhook_alert_url), so it can be verified before a real outage. Returns 422 when no destination is set and 502 with the upstream error when the destination rejects the message.\n\n_Policy note: Sends a synthetic failure notification to the configured destination_"
          }
        },
        {
          "name": "Replay a dead-letter delivery",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/webhooks/deliveries/:deliveryId/replay",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "webhooks",
                "deliveries",
                ":deliveryId",
                "replay"
              ],
              "variable": [
                {
                  "key": "deliveryId",
                  "value": "{{deliveryId}}",
                  "description": "Webhook delivery id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRe-signs the retained raw payload and resets the retry budget for a dead-letter delivery.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Webhook delivery id. |"
          }
        }
      ]
    },
    {
      "name": "Sanctions",
      "item": [
        {
          "name": "Get client watchlist",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/custom-lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "custom-lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the client-managed watchlist with schema, column definitions, and entity count."
          }
        },
        {
          "name": "Upload client watchlist",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/custom-lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "custom-lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUploads a CSV or XLSX file to replace the client-managed watchlist. Replaces the entire list atomically."
          }
        },
        {
          "name": "Toggle client watchlist",
          "request": {
            "method": "PATCH",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/custom-lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "custom-lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nEnables or disables the client-managed watchlist for screening."
          }
        },
        {
          "name": "Get CSV template",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/custom-lists/template",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "custom-lists",
                "template"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns a CSV template showing the required columns for the client watchlist import."
          }
        },
        {
          "name": "List sanctions entities",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/entities",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "entities"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns all watchlist entities currently loaded across all active sources."
          }
        },
        {
          "name": "Ingest freshness stats",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "ingest"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns current sanctions data freshness: last-updated timestamps per source and total entity count."
          }
        },
        {
          "name": "Trigger list ingestion",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "ingest"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nTriggers a fresh download and ingestion of one or all sanctions lists. Accepts an optional { source } to refresh a single list. In production this runs on a 6-hour cron."
          }
        },
        {
          "name": "List watchlist sources",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the configured watchlist sources with operational status, entity counts, and provider mode."
          }
        },
        {
          "name": "Refresh watchlists",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nTriggers a manual refresh of all watchlist feeds. Returns the diff summary (added, updated, removed per source)."
          }
        },
        {
          "name": "Toggle a watchlist",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/lists",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "lists"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nEnables or disables a sanctions watchlist source. Disabled sources are skipped during screening.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"source\": \"OFAC_SDN\",\n  \"enabled\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "List whitelisted matches",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/matches",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "matches"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the false-positive whitelist — (entity, account) pairs that analysts have cleared so the match is suppressed on future screenings."
          }
        },
        {
          "name": "Clear a match (false positive)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/matches",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "matches"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nMarks a sanctions match as a confirmed false positive, adding it to the whitelist so the match is suppressed on future screens.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"entity_id\": \"string\",\n  \"account\": \"string\",\n  \"query_name\": \"string\",\n  \"reason\": \"string\",\n  \"by\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Re-activate a match",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/matches?match_id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "matches"
              ],
              "query": [
                {
                  "key": "match_id",
                  "value": "",
                  "disabled": true,
                  "description": "The whitelist entry id to remove."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRemoves a whitelist entry, re-activating the sanctions match for future screenings. Requires the match_id query parameter.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `match_id` | string | yes | The whitelist entry id to remove. |"
          }
        },
        {
          "name": "Continuous re-screening",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/rescreen",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "rescreen"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRe-runs sanctions screening across the full stored history against the current lists + whitelist. Opens cases for counterparties that newly match after a list update."
          }
        },
        {
          "name": "Ad-hoc name screening",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/sanctions/screen",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sanctions",
                "screen"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie.\n\nScreens one or more names against the active watchlists in real time — for KYC onboarding, pre-payout checks, or on-demand queries. Returns match results with scores and hit/monitor/clear decisions per name.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `name` | string | no | A single name to screen (alternative to names array). |\n| `names` | string[] | no | Multiple names to screen in one request. |\n| `min_score` | number | no | Minimum score (0–1) to surface weaker candidates. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"string\",\n  \"names\": [],\n  \"min_score\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Reporting",
      "item": [
        {
          "name": "Dashboard metrics",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/metrics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "metrics"
              ]
            },
            "description": "**Auth:** API key only (`Authorization: Bearer {{apiKey}}`). A console session is *not* accepted.\n\nAggregated reporting metrics: totals, flag rate, top triggered rules, and 14-day trend data.\n\n_Policy note: Prometheus scrape, gated by METRICS_SCRAPE_TOKEN; exposes the build version so never public_"
          }
        },
        {
          "name": "List SAR drafts",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sars",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sars"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nSuspicious Activity Report drafts, generated from escalated alert dispositions. Each draft includes subject, counterparty, matched rules, and a generated narrative ready for compliance review. Export from the SAR Drafts console page."
          }
        },
        {
          "name": "Export SAR draft",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/sars/:sarId/export",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sars",
                ":sarId",
                "export"
              ],
              "variable": [
                {
                  "key": "sarId",
                  "value": "{{sarId}}",
                  "description": "SAR draft id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `compliance_officer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the SAR draft in a downloadable format suitable for filing with the regulator.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | SAR draft id. |"
          }
        },
        {
          "name": "Advance SAR workflow",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/sars/:sarId/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "sars",
                ":sarId",
                "status"
              ],
              "variable": [
                {
                  "key": "sarId",
                  "value": "{{sarId}}",
                  "description": "SAR draft id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `compliance_officer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nAdvances the SAR sign-off workflow: draft → under_review → approved → submitted. The approved/submitted transitions require admin role.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | SAR draft id. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"under_review\",\n  \"note\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Onboarding",
      "item": [
        {
          "name": "Request a demo",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/demo-requests",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "demo-requests"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\nLanding-page demo intake. Records the request against the activity trail and routes it to a solutions engineer. Provisions nothing — instances are deployed per institution, not signed up for.\n\n_Policy note: Landing-page demo intake; provisions nothing, records a lead to the activity trail — rate limited + size bounded per IP_",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"contactName\": \"string\",\n  \"workEmail\": \"user@example.com\",\n  \"institution\": \"string\",\n  \"country\": \"\",\n  \"role\": \"\",\n  \"deploymentInterest\": \"managed\",\n  \"volumeBand\": \"not-sure\",\n  \"message\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "Get onboarding checklist",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/onboarding/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "onboarding",
                "status"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the first-run checklist state — derived from what the org has actually done: created a test key, screened a transaction, activated a rule, and reviewed a sanctions match."
          }
        },
        {
          "name": "Dismiss or restore checklist",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/onboarding/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "onboarding",
                "status"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nDismisses the onboarding checklist so it does not show on subsequent visits, or restores it if previously dismissed.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"dismissed\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Settings",
      "item": [
        {
          "name": "List API keys",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "api-keys"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists the API keys with masked values, environment, and last-used timestamps."
          }
        },
        {
          "name": "Create API key",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/settings/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "api-keys"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nSandbox keys return HTTP 201 with outcome created and the plaintext key once. Live keys return HTTP 202 with outcome pending_approval and are created only after approval by a different administrator. The approving checker receives the plaintext live key once in the approval response; subsequent reads never expose it.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `label` | string | no | Human-readable label, e.g. \"Core banking integration\". |\n| `environment` | enum | no | test | live (default test) |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"label\": \"\",\n  \"environment\": \"\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Rotate an API key",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/settings/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "api-keys"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRotates an API key, producing a new key and optionally keeping the old one alive for a grace period. Live key rotations go through maker-checker approval.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"action\": \"string\",\n  \"id\": \"string\",\n  \"grace_minutes\": -9007199254740991,\n  \"label\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Revoke API key",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/api-keys?id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "api-keys"
              ],
              "query": [
                {
                  "key": "id",
                  "value": "",
                  "disabled": true,
                  "description": "The key id to revoke."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRevokes an API key by id. Revoked keys stop authenticating immediately.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | The key id to revoke. |"
          }
        },
        {
          "name": "List invitations",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/invitations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "invitations"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists pending and accepted team invitations, including invitee email addresses. Admin only.\n\n_Policy note: Lists invitee emails and roles — admin only, matching invitation creation_"
          }
        },
        {
          "name": "Create invitation",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/settings/invitations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "invitations"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nInvites a user to join the team. The invitee receives an email with an acceptance link. Rate-limited to 10 invites/minute and 100 invites/day.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"name\": \"string\",\n  \"role\": \"analyst\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Get organization",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/organization",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "organization"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the institution profile: name, country, institution type, and active API key count. Raw API keys are never returned from this endpoint."
          }
        },
        {
          "name": "Update organization",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/settings/organization",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "organization"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUpdates organization profile fields.\n\n**Body parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `name` | string | no | Institution name (2–80 chars). |\n| `country` | string | no | ISO 3166-1 alpha-2 country code. |\n| `institution_type` | string | no | bank | psp | mfi | sacco | fintech |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Umoja Payments Ltd\",\n  \"country\": \"KE\",\n  \"institution_type\": \"psp\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Get retention policy",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/retention",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "retention"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the current data retention policy with jurisdiction presets, category labels, and the PII field inventory."
          }
        },
        {
          "name": "Update retention policy",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/settings/retention",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "retention"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nUpdates the data retention policy. The jurisdiction preset and per-category retention days are validated against statutory minimums.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"jurisdiction_preset\": \"string\",\n  \"categories\": null,\n  \"note\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Activity",
      "item": [
        {
          "name": "Activity / audit log",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/activity?category=&status=&resource_type=&q=&since=&limit=&offset=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "activity"
              ],
              "query": [
                {
                  "key": "category",
                  "value": "",
                  "disabled": true,
                  "description": "user_action | api_request | system"
                },
                {
                  "key": "status",
                  "value": "",
                  "disabled": true,
                  "description": "success | denied | error"
                },
                {
                  "key": "resource_type",
                  "value": "",
                  "disabled": true,
                  "description": "Filter by resource, e.g. rule, case, sanctions_match, api_key, screening."
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true,
                  "description": "Free-text search across summary, action, actor, path, and resource id."
                },
                {
                  "key": "since",
                  "value": "",
                  "disabled": true,
                  "description": "ISO timestamp lower bound."
                },
                {
                  "key": "limit",
                  "value": "",
                  "disabled": true,
                  "description": "Page size (default 100, max 200)."
                },
                {
                  "key": "offset",
                  "value": "",
                  "disabled": true,
                  "description": "Pagination offset."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the unified, append-only activity log — both console user actions (rule edits, case decisions, watchlist toggles, key management) and API requests (screening calls with method, path, status, latency, and the calling API key). Time-ordered newest-first and immutable: entries are never modified or deleted through the API. Use it for compliance audit trails, incident forensics, and usage monitoring.\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `category` | enum | no | user_action | api_request | system |\n| `status` | enum | no | success | denied | error |\n| `resource_type` | string | no | Filter by resource, e.g. rule, case, sanctions_match, api_key, screening. |\n| `q` | string | no | Free-text search across summary, action, actor, path, and resource id. |\n| `since` | string | no | ISO timestamp lower bound. |\n| `limit` | number | no | Page size (default 100, max 200). |\n| `offset` | number | no | Pagination offset. |"
          }
        }
      ]
    },
    {
      "name": "Meta",
      "item": [
        {
          "name": "Get console environment",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/environment",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "environment"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the currently selected console environment (test or live)."
          }
        },
        {
          "name": "Switch console environment",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/environment",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "environment"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nSwitches the console environment between test and live. Persisted in an httpOnly cookie.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"environment\": \"test\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Health check",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/health",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "health"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\nLightweight health check returning overall status, storage mode (mock or database), database connectivity, loaded rule count, and a coarse per-dependency status list. Probes local dependencies only. Returns 503 when the database is configured but unreachable; a degraded Redis or sanctions provider still returns 200. Used by load balancers and monitoring.\n\n_Policy note: Liveness + DB-readiness probe; no institution data_",
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "Dependency health detail",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/health/dependencies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "health",
                "dependencies"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFull live status for every external dependency — Postgres, Redis, and the sanctions provider — with per-probe latency, error codes, and remediation hints. Distinguishes not_configured (a supported deployment mode) from down (a fault). The outbound sanctions provider check is cached for roughly 60 seconds. Always returns 200 when authorized; /api/health owns the 503 semantics.\n\n_Policy note: Live dependency detail: latency, error codes, provider endpoint_"
          }
        },
        {
          "name": "OpenAPI 3.1 spec",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/openapi?format=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "openapi"
              ],
              "query": [
                {
                  "key": "format",
                  "value": "",
                  "disabled": true,
                  "description": "json — return the spec as JSON instead of YAML."
                }
              ]
            },
            "description": "**Auth:** public — no credential required.\n\nThe machine-readable OpenAPI 3.1 specification for this API, generated from the same endpoint catalog that renders these docs — so the spec and the docs can never drift. YAML by default; use it to generate typed clients, run contract tests, or import into Postman/Insomnia.\n\n_Policy note: Published API spec; no institution data_\n\n**Query parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `format` | enum | no | json — return the spec as JSON instead of YAML. |",
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "Readiness probe",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/readiness",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "readiness"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReadiness probe — returns 200 when the server is ready to accept traffic."
          }
        }
      ]
    },
    {
      "name": "Feedback",
      "item": [
        {
          "name": "List feedback labels",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/feedback",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "feedback"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists analyst feedback labels (true_positive, false_positive, suspicious) plus aggregated stats — newest first."
          }
        },
        {
          "name": "Label a screening",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/feedback",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "feedback"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nManually labels a screening record for analyst review feedback. Case resolutions and SAR filings write labels automatically; this endpoint covers ad-hoc labeling.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"screening_id\": \"string\",\n  \"label\": \"true_positive\",\n  \"note\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Export feedback labels",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/feedback/export",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "feedback",
                "export"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nExports all feedback labels for downstream consumption."
          }
        }
      ]
    },
    {
      "name": "Approvals",
      "item": [
        {
          "name": "List approval requests",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/approvals",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "approvals"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nLists maker-checker approval requests for pending, approved, rejected, and cancelled items. Used by administrators to review and act on change requests."
          }
        },
        {
          "name": "Fetch approval request",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/approvals/:approvalId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "approvals",
                ":approvalId"
              ],
              "variable": [
                {
                  "key": "approvalId",
                  "value": "{{approvalId}}",
                  "description": "Approval request id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nFetches a single approval request by id.\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Approval request id. |"
          }
        },
        {
          "name": "Decide an approval",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/approvals/:approvalId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "approvals",
                ":approvalId"
              ],
              "variable": [
                {
                  "key": "approvalId",
                  "value": "{{approvalId}}",
                  "description": "Approval request id."
                }
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `admin`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nApproves, rejects, or cancels a pending approval request. Live API key creations and rotations require a second admin to approve.\n\n_Policy note: Maker-checker approval_\n\n**Path parameters**\n\n| Name | Type | Required | Description |\n| --- | --- | --- | --- |\n| `id` | string | yes | Approval request id. |",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"decision\": \"approve\",\n  \"reason\": \"Reviewed against the corridor policy — approved.\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Sweep for overdue approvals",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/approvals/sweep",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "approvals",
                "sweep"
              ]
            },
            "description": "**Auth:** API key only (`Authorization: Bearer {{apiKey}}`). A console session is *not* accepted.\n\nScans pending approval requests and sends the time-based notifications: an escalation once a request has gone unactioned past APPROVAL_ESCALATE_AFTER_HOURS (default 24), and a warning before it lapses at its 7-day expiry. Intended for a scheduler on an hourly cadence. Idempotent — each stage is recorded per request, so repeated sweeps never notify twice. Returns { examined, notified, stages }.\n\n_Policy note: Scheduled sweep for overdue/expiring approvals; a cron cannot hold a console session_"
          }
        }
      ]
    },
    {
      "name": "Analytics",
      "item": [
        {
          "name": "Rule / typology effectiveness",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/analytics/effectiveness",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "analytics",
                "effectiveness"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nPer-rule or per-typology effectiveness over time: triggers, hit rate, and precision derived from feedback labels. Supports 7d, 30d, 90d, and all-time windows."
          }
        },
        {
          "name": "Backtest a rule or typology",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/backtest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "backtest"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReplays historical production screenings through a candidate rule/typology YAML and diffs the decision against the live config. Returns newly flagged, no-longer-flagged counts, label breakdowns, and example transactions.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"yaml\": \"string\",\n  \"kind\": \"rule\",\n  \"window\": \"30d\",\n  \"limit\": 500\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        },
        {
          "name": "Get latest benchmark",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/benchmark",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "benchmark"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nReturns the most recent benchmark result (latency percentiles) or null if none has been run."
          }
        },
        {
          "name": "Run a benchmark",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/benchmark",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "benchmark"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `analyst`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nRuns the screening engine through N iterations to produce latency percentiles (p50, p75, p95, p99) and a histogram.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"n\": 50\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Account",
      "item": [
        {
          "name": "Accept an invitation",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/invitations/accept",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "invitations",
                "accept"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\nAccepts a team invitation using the token from the invite URL. Requires an authenticated session with a verified identity provider.\n\n_Policy note: Invited user must be signed in; also admits an invitee with no role yet (the only such route); token and email match enforced in acceptInvitation()_",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"string\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Other",
      "item": [
        {
          "name": "GET /api",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\n_Policy note: Service index: no institution data, no build version_",
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "POST /api/csp-report",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/csp-report",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "csp-report"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\n_Policy note: Browser CSP violation reports; reads and writes no institution data — rate limited + 8 KB cap_",
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "GET /metrics",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/metrics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "metrics"
              ]
            },
            "description": "**Auth:** API key only (`Authorization: Bearer {{apiKey}}`). A console session is *not* accepted.\n\n_Policy note: Prometheus scrape, gated by METRICS_SCRAPE_TOKEN; exposes the build version so never public_"
          }
        }
      ]
    },
    {
      "name": "Metrics",
      "item": [
        {
          "name": "GET /api/analytics/metrics",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/analytics/metrics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "analytics",
                "metrics"
              ]
            },
            "description": "**Auth:** API key (`Authorization: Bearer {{apiKey}}`) or a console session cookie."
          }
        }
      ]
    },
    {
      "name": "Logout",
      "item": [
        {
          "name": "GET /api/auth/logout",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "logout"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\n_Policy note: Hands the browser to the IdP end-session endpoint_",
            "auth": {
              "type": "noauth"
            }
          }
        },
        {
          "name": "POST /api/auth/logout",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "logout"
              ]
            },
            "description": "**Auth:** public — no credential required.\n\n_Policy note: Ends the console session; same-origin enforced in-handler_",
            "auth": {
              "type": "noauth"
            }
          }
        }
      ]
    },
    {
      "name": "Sessions",
      "item": [
        {
          "name": "GET /api/settings/sessions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/sessions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "sessions"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\n_Policy note: Active session list — admins see all institution sessions, other roles only their own (enforced in-handler)_"
          }
        },
        {
          "name": "DELETE /api/settings/sessions",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/sessions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "sessions"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\n_Policy note: Revoke one session — own session for non-admins, any institution session for admins (enforced in-handler)_"
          }
        },
        {
          "name": "POST /api/settings/sessions/revoke-all",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/settings/sessions/revoke-all",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "settings",
                "sessions",
                "revoke-all"
              ]
            },
            "description": "**Auth:** console session cookie required (minimum role: `viewer`). An API key alone returns 401 — run **Auth / 2. Sign in** first.\n\n_Policy note: Revoke all sessions of a user — own for non-admins, any institution user for admins (enforced in-handler)_"
          }
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "http://localhost:3000",
      "type": "string"
    }
  ]
}
