openapi: 3.1.0
info:
  title: Mulisa Screening API
  version: 1.0.0
  description: "Real-time AML transaction screening: rule-first decisioning with blacklists, typed YAML rules, behavioral
    profiles, and advisory ML scores. Generated from the endpoint catalog — the same source that drives the interactive
    console docs. Component schemas are derived from Zod (lib/api-zod-schemas/) so shapes always match the runtime."
  contact:
    name: JengaCore
    url: https://aml.jengacore.com
servers:
  - url: /api
    description: Console origin — same host, no CORS
security:
  - bearerAuth: []
  - apiKeyHeader: []
tags:
  - name: Screening
  - name: Transactions
  - name: Cases
  - name: Rules
  - name: Typologies
  - name: Sandbox
  - name: Blacklists
  - name: Profiles
  - name: Networks
  - name: Webhooks
  - name: Sanctions
  - name: Reporting
  - name: Onboarding
  - name: Settings
  - name: Activity
  - name: Meta
  - name: Feedback
  - name: Approvals
  - name: Analytics
  - name: Account
paths:
  /v1/screen:
    post:
      operationId: postScreen
      summary: Screen a transaction
      description: >-
        Screens 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, then the advisory ML 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; see docs/screening-errors.md.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Screening
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ScreeningRequest"
            example:
              transaction:
                id: txn_20260707_0042
                amount: 1250000
                currency: KES
                type: transfer
                channel: api
                timestamp: 2026-07-07T09:14:00Z
                reference: INV-2211
                country_from: KE
                country_to: NG
              sender:
                id: usr_8f2k1
                name: Amina Odhiambo
                account: "0114589022"
                institution_id: KCBLKENX
                country: KE
                kyc_level: standard
                risk_score: 0.35
                pep: false
              receiver:
                id: usr_m1092
                name: Chidi Nwosu
                account: "3308812304"
                institution_id: GTBINGLA
                country: NG
                kyc_level: basic
              metadata:
                device_id: dev_a81xz
                ip: 105.163.2.11
                geo: Nairobi
                narrative: invoice payment
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ScreeningResponse"
              example:
                screening_id: scr_x81ka02f
                status: flagged
                confidence: 0.82
                risk_score: 0.7
                risk_threshold: 0.6
                rule_results:
                  - rule_id: high-value-cross-border
                    name: High-value cross-border transfer
                    matched: true
                    weight: 0.7
                    severity: high
                    detail: Transfers above 1M crossing borders require review.
                model_score: 0.47
                blacklist_hits: []
                latency_ms: 12
                timestamp: 2026-07-07T09:14:01Z
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/screen/iso20022:
    post:
      operationId: postScreenIso20022
      summary: Screen an ISO 20022 pacs.008
      description: >-
        Screens 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 /v1/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.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Screening
      x-beta: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                Document.FIToFICstmrCdtTrf:
                  type: object
                  description: 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.
              required:
                - Document.FIToFICstmrCdtTrf
            example:
              Document:
                FIToFICstmrCdtTrf:
                  GrpHdr:
                    MsgId: MSG-20260707-001
                    CreDtTm: 2026-07-07T09:14:00Z
                    NbOfTxs: "1"
                  CdtTrfTxInf:
                    PmtId:
                      EndToEndId: E2E-2211
                      TxId: txn_20260707_0042
                      UETR: a1b2c3d4-...
                    IntrBkSttlmAmt:
                      Ccy: KES
                      value: "1250000"
                    IntrBkSttlmDt: 2026-07-07
                    Dbtr:
                      Nm: Amina Odhiambo
                    DbtrAcct:
                      Id:
                        Othr:
                          Id: "0114589022"
                    DbtrAgt:
                      FinInstnId:
                        BICFI: KCBLKENX
                    Cdtr:
                      Nm: Chidi Nwosu
                    CdtrAcct:
                      Id:
                        Othr:
                          Id: "3308812304"
                    CdtrAgt:
                      FinInstnId:
                        BICFI: GTBINGLA
                    RmtInf:
                      Ustrd:
                        - invoice payment
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/screen/mojaloop:
    post:
      operationId: postScreenMojaloop
      summary: Screen a Mojaloop transaction
      description: >-
        Screens 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 /v1/screen.
        Designed for a Mojaloop hub to screen inline. The response adds a mojaloop block (transaction_id, quote_id) and
        the normalized_request.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Screening
      x-beta: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                transactionId:
                  type: string
                  description: Mojaloop transaction identifier.
                amount:
                  type: object
                  description: '{ amount, currency } — e.g. { "amount": "1250000", "currency": "KES" }.'
                payer:
                  type: object
                  description: "Payer party: { partyIdInfo: { partyIdType, partyIdentifier, fspId }, name?, personalInfo? }."
                payee:
                  type: object
                  description: "Payee party: same shape as payer."
                transactionType:
                  type: object
                  description: "{ scenario, initiator, initiatorType } — scenario maps to the canonical transaction type."
                note:
                  type: string
                  description: Free-text note, mapped to metadata.narrative.
              required:
                - transactionId
                - amount
                - payer
                - payee
            example:
              transactionId: b51ec534-ee48-4575-b6a9-ead2955b8069
              quoteId: 7c23e6c0-1a2b-4f3d-9e8a-2b1c0d4e5f6a
              amount:
                amount: "1250000"
                currency: KES
              transactionType:
                scenario: TRANSFER
                initiator: PAYER
                initiatorType: CONSUMER
              payer:
                partyIdInfo:
                  partyIdType: MSISDN
                  partyIdentifier: "254712345678"
                  fspId: umojafsp
                name: Amina Odhiambo
              payee:
                partyIdInfo:
                  partyIdType: MSISDN
                  partyIdentifier: "233201234567"
                  fspId: gtbfsp
                name: Chidi Nwosu
              note: invoice payment
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/screen/iso8583:
    post:
      operationId: postScreenIso8583
      summary: Screen an ISO 8583 message
      description: >-
        Screens 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.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Screening
      x-beta: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                mti:
                  type: string
                  description: 4-digit message type indicator (e.g. "0100", "0200").
                fields:
                  type: object
                  description: 'Bitmap-indexed data elements keyed by field number (e.g. "2": "..." for PAN).'
              required:
                - mti
                - fields
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/transactions:
    get:
      operationId: getTransactions
      summary: List screened transactions
      description: >-
        Lists 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.


        **Auth:** Console session (any role).
      tags:
        - Transactions
      parameters:
        - name: status
          in: query
          required: false
          description: cleared | flagged
          schema:
            type: string
            enum:
              - cleared
              - flagged
        - name: type
          in: query
          required: false
          description: Transaction type filter (transfer, card, cash_deposit, remittance, mobile_money).
          schema:
            type: string
        - name: channel
          in: query
          required: false
          description: api | mobile | branch | agent
          schema:
            type: string
            enum:
              - api
              - mobile
              - branch
              - agent
        - name: currency
          in: query
          required: false
          description: Currency code filter, e.g. KES.
          schema:
            type: string
        - name: country
          in: query
          required: false
          description: Corridor filter — matches country_from OR country_to.
          schema:
            type: string
        - name: reviewed
          in: query
          required: false
          description: true | false
          schema:
            type: boolean
        - name: disposition
          in: query
          required: false
          description: true_positive | false_positive | escalated
          schema:
            type: string
            enum:
              - true_positive
              - false_positive
              - escalated
        - name: q
          in: query
          required: false
          description: Full-text search on id, names, and accounts.
          schema:
            type: string
        - name: date_from
          in: query
          required: false
          description: Inclusive lower bound on transaction timestamp (ISO date).
          schema:
            type: string
        - name: date_to
          in: query
          required: false
          description: Inclusive upper bound; date-only values mean end of day.
          schema:
            type: string
        - name: amount_min
          in: query
          required: false
          description: Minimum transaction amount.
          schema:
            type: number
        - name: amount_max
          in: query
          required: false
          description: Maximum transaction amount.
          schema:
            type: number
        - name: risk_min
          in: query
          required: false
          description: Minimum risk_score (0..1).
          schema:
            type: number
        - name: limit
          in: query
          required: false
          description: Page size, max 500 (default 25).
          schema:
            type: number
        - name: offset
          in: query
          required: false
          description: Zero-based pagination offset.
          schema:
            type: number
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchTransactions
      summary: Review / disposition a record
      description: >-
        Marks a screening record as reviewed and/or sets the analyst disposition. Dispositions are the training labels
        for the Phase 2 supervised model — see docs/ml-roadmap.md. Escalated dispositions generate SAR drafts.


        **Auth:** Console session — disposition requires the analyst role.
      tags:
        - Transactions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                screening_id:
                  type: string
                  description: The screening decision to update.
                reviewed:
                  type: boolean
                  description: Mark the record reviewed.
                disposition:
                  type: string
                  enum:
                    - true_positive
                    - false_positive
                    - escalated
                  description: true_positive | false_positive | escalated
                note:
                  type: string
                  description: Optional analyst note, included in SAR drafts.
              required:
                - screening_id
            example:
              screening_id: scr_x81ka02f
              reviewed: true
              disposition: escalated
              note: Structuring pattern across 3 counterparties.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/transactions/import:
    post:
      operationId: postTransactionsImport
      summary: Bulk-import transactions
      description: >-
        Bulk-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.


        **Auth:** Console session — analyst role required.
      tags:
        - Transactions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TransactionImport"
            example:
              raw: string
              filename: string
              dry_run: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/cases:
    get:
      operationId: getCases
      summary: List cases
      description: >-
        Lists 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.


        **Auth:** Console session (any role).
      tags:
        - Cases
      parameters:
        - name: status
          in: query
          required: false
          description: open | investigating | escalated | closed
          schema:
            type: string
            enum:
              - open
              - investigating
              - escalated
              - closed
        - name: priority
          in: query
          required: false
          description: low | medium | high | critical
          schema:
            type: string
            enum:
              - low
              - medium
              - high
              - critical
        - name: assignee
          in: query
          required: false
          description: Analyst id, or "unassigned".
          schema:
            type: string
        - name: institution
          in: query
          required: false
          description: Emitting FSP (institution_id) of the case subject.
          schema:
            type: string
        - name: sla
          in: query
          required: false
          description: breached — only cases past their SLA due time.
          schema:
            type: string
        - name: q
          in: query
          required: false
          description: Search on case id, subject name, or subject account.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CaseList"
              example:
                items: []
                total: -9007199254740991
                stats:
                  open: -9007199254740991
                  investigating: -9007199254740991
                  escalated: -9007199254740991
                  closed: -9007199254740991
                institutions: []
                assignees: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postCases
      summary: Open a case manually
      description: >-
        Opens 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Cases
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  description: Human-readable case title.
                subject:
                  type: object
                  description: Case subject. Requires subject.account; optional name, institution_id, country, role (sender | receiver).
                priority:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                    - critical
                  description: low | medium | high | critical (default medium). Sets the SLA due time.
                note:
                  type: string
                  description: Optional opening note added to the timeline.
              required:
                - title
                - subject
            example:
              title: Regulator referral — suspected mule ring
              priority: high
              subject:
                account: "0114589022"
                name: Amina Odhiambo
                institution_id: KCBLKENX
                country: KE
                role: receiver
              note: Referred by UIF on 2026-07-08.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Case"
              example:
                id: string
                environment: test
                subject:
                  account: string
                  name: string
                  institution_id: string
                  country: string
                  role: sender
                title: string
                status: open
                priority: low
                source: rule
                assignee: string
                alerts: []
                events: []
                resolution: confirmed_suspicious
                resolution_note: string
                sla_due_at: string
                created_at: string
                updated_at: string
                closed_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/cases/{id}:
    get:
      operationId: getCase
      summary: Fetch a case
      description: >-
        Fetches 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.


        **Auth:** Console session (any role).
      tags:
        - Cases
      parameters:
        - name: id
          in: path
          required: true
          description: Case id (e.g. case_20260705_0015).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Case"
              example:
                id: string
                environment: test
                subject:
                  account: string
                  name: string
                  institution_id: string
                  country: string
                  role: sender
                title: string
                status: open
                priority: low
                source: rule
                assignee: string
                alerts: []
                events: []
                resolution: confirmed_suspicious
                resolution_note: string
                sla_due_at: string
                created_at: string
                updated_at: string
                closed_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchCase
      summary: Act on a case
      description: >-
        Performs 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).


        **Auth:** Console session — analyst role required.
      tags:
        - Cases
      parameters:
        - name: id
          in: path
          required: true
          description: Case id.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - open
                    - investigating
                    - escalated
                    - closed
                  description: open | investigating | escalated | closed (validated transition).
                assignee:
                  type: string
                  description: Assign / reassign to an analyst; empty string unassigns.
                priority:
                  type: string
                  enum:
                    - low
                    - medium
                    - high
                    - critical
                  description: low | medium | high | critical — re-derives the SLA due time.
                note:
                  type: string
                  description: Append an investigation note.
                resolution:
                  type: string
                  enum:
                    - confirmed_suspicious
                    - false_positive
                    - reported
                    - no_action
                  description: confirmed_suspicious | false_positive | reported | no_action — closes the case.
                resolution_note:
                  type: string
                  description: Optional rationale recorded with the resolution.
            example:
              resolution: reported
              resolution_note: STR filed with UIF, ref 2026-0442.
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/rules:
    get:
      operationId: getRules
      summary: List rules
      description: >-
        Lists all rules with status, version, and live performance stats (trigger_count, hit_rate — updated on every
        screening).


        **Auth:** Console session (any role).
      tags:
        - Rules
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RuleList"
              example:
                items: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postRules
      summary: Create a rule
      description: >-
        Creates 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Rules
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                yaml:
                  type: string
                  description: Rule definition in typed YAML.
                status:
                  type: string
                  description: draft (default) | active | disabled
              required:
                - yaml
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rule"
              example:
                id: string
                name: string
                description: string
                severity: low
                weight: 0
                status: active
                version: -9007199254740991
                yaml: string
                trigger_count: 0
                hit_rate: 0
                created_at: string
                updated_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/rules/bulk:
    post:
      operationId: postRulesBulk
      summary: Bulk create / upsert rules
      description: >-
        Creates 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Rules
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                yaml:
                  type: string
                  description: Multiple rule documents separated by "---". Provide this OR "rules".
                rules:
                  type: array
                  items: {}
                  description: Array of rule YAML strings, or objects { yaml, status }. Provide this OR "yaml".
                on_conflict:
                  type: string
                  description: skip (default) keeps existing; replace upserts (idempotent); error fails conflicting items.
                atomic:
                  type: boolean
                  description: When true, validate everything first and apply nothing if any item errors. Default false.
                default_status:
                  type: string
                  description: draft (default) | active — status for items that don’t set their own.
            example:
              on_conflict: replace
              default_status: active
              yaml: |-
                rule:
                  id: high-value-cross-border
                  name: High-value cross-border
                  severity: high
                  weight: 0.7
                  when:
                    all:
                      - field: transaction.amount
                        op: gt
                        value: 1000000
                ---
                rule:
                  id: dormant-account-burst
                  name: Dormant account burst
                  severity: medium
                  weight: 0.5
                  when:
                    all:
                      - field: profile.new_counterparty
                        op: eq
                        value: true
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
              example:
                applied: true
                summary:
                  total: 2
                  created: 1
                  replaced: 1
                  skipped: 0
                  errors: 0
                results:
                  - index: 0
                    id: high-value-cross-border
                    name: High-value cross-border
                    outcome: replaced
                  - index: 1
                    id: dormant-account-burst
                    name: Dormant account burst
                    outcome: created
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/rules/{id}:
    get:
      operationId: getRule
      summary: Fetch a rule
      description: |-
        Fetches a single rule by id, including its YAML source and stats.

        **Auth:** Console session (any role).
      tags:
        - Rules
      parameters:
        - name: id
          in: path
          required: true
          description: Rule id (matches the id in the YAML).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rule"
              example:
                id: string
                name: string
                description: string
                severity: low
                weight: 0
                status: active
                version: -9007199254740991
                yaml: string
                trigger_count: 0
                hit_rate: 0
                created_at: string
                updated_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: putRule
      summary: Update a rule
      description: >-
        Updates 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Rules
      parameters:
        - name: id
          in: path
          required: true
          description: Rule id.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                yaml:
                  type: string
                  description: New YAML (validated; bumps version).
                status:
                  type: string
                  enum:
                    - active
                    - draft
                    - disabled
                  description: active | draft | disabled
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteRule
      summary: Delete a rule
      description: |-
        Permanently deletes a rule.

        **Auth:** Console session — admin role required.
      tags:
        - Rules
      parameters:
        - name: id
          in: path
          required: true
          description: Rule id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/rules/{id}/versions:
    get:
      operationId: getRuleVersions
      summary: List rule versions
      description: >-
        Returns 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.


        **Auth:** Console session (any role).
      tags:
        - Rules
      parameters:
        - name: id
          in: path
          required: true
          description: Rule id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/typologies:
    get:
      operationId: getTypologies
      summary: List typologies
      description: |-
        Lists all typologies with status, version, and live trigger stats.

        **Auth:** Console session (any role).
      tags:
        - Typologies
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TypologyList"
              example:
                items: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postTypologies
      summary: Create a typology
      description: >-
        Creates a typology from typed YAML. The YAML is validated for structure, weights, and thresholds, and every
        referenced rule_id must already exist.


        **Auth:** Console session — analyst role required.
      tags:
        - Typologies
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                yaml:
                  type: string
                  description: Typology definition in typed YAML.
                status:
                  type: string
                  description: draft (default) | active
              required:
                - yaml
            example:
              yaml: |-
                typology:
                  id: money-mule-receiver
                  name: Money mule receiver
                  category: money_mule
                  rules:
                    - rule_id: rapid-inbound-velocity
                      weight: 0.4
                    - rule_id: new-sender-burst
                      weight: 0.3
                    - rule_id: pass-through-account
                      weight: 0.3
                  alert_threshold: 0.6
                  interdict_threshold: 0.9
              status: active
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Typology"
              example:
                id: string
                name: string
                description: string
                category: string
                rules: []
                alert_threshold: 0
                interdict_threshold: 0
                status: active
                version: -9007199254740991
                yaml: string
                trigger_count: 0
                created_at: string
                updated_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/typologies/{id}:
    get:
      operationId: getTypology
      summary: Fetch a typology
      description: |-
        Fetches a single typology by id, including its YAML source, member rules, thresholds, and stats.

        **Auth:** Console session (any role).
      tags:
        - Typologies
      parameters:
        - name: id
          in: path
          required: true
          description: Typology id (matches the id in the YAML).
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Typology"
              example:
                id: string
                name: string
                description: string
                category: string
                rules: []
                alert_threshold: 0
                interdict_threshold: 0
                status: active
                version: -9007199254740991
                yaml: string
                trigger_count: 0
                created_at: string
                updated_at: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: putTypology
      summary: Update a typology
      description: >-
        Updates 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Typologies
      parameters:
        - name: id
          in: path
          required: true
          description: Typology id.
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                yaml:
                  type: string
                  description: New YAML (validated; bumps version).
                status:
                  type: string
                  enum:
                    - active
                    - draft
                    - disabled
                  description: active | draft | disabled
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteTypology
      summary: Delete a typology
      description: |-
        Permanently deletes a typology. Member rules are unaffected.

        **Auth:** Console session — admin role required.
      tags:
        - Typologies
      parameters:
        - name: id
          in: path
          required: true
          description: Typology id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/typologies/{id}/versions:
    get:
      operationId: getTypologyVersions
      summary: List typology versions
      description: >-
        Returns 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.


        **Auth:** Console session (any role).
      tags:
        - Typologies
      parameters:
        - name: id
          in: path
          required: true
          description: Typology id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sandbox/run:
    post:
      operationId: postSandboxRun
      summary: Run a sandbox simulation
      description: |-
        Runs transactions through the full engine without persisting anything to reporting history.

        **Auth:** Console session (any role).
      tags:
        - Sandbox
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                transactions:
                  type: array
                  items: {}
                  description: ScreeningRequest[] to evaluate.
                rule_ids:
                  type: array
                  items:
                    type: string
                  description: Restrict evaluation to specific rules (defaults to all active).
                include_drafts:
                  type: boolean
                  description: Also evaluate draft rules.
                yaml:
                  type: string
                  description: Ad-hoc rule YAML to test without saving.
                yaml_only:
                  type: boolean
                  description: Evaluate only the ad-hoc YAML rule.
              required:
                - transactions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sandbox/sample:
    get:
      operationId: getSandboxSample
      summary: Get sample transactions
      description: |-
        Returns a ready-made set of representative sample transactions for use as sandbox input.

        **Auth:** Console session (any role).
      tags:
        - Sandbox
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sandbox/scenarios:
    get:
      operationId: getSandboxScenarios
      summary: Get test scenarios
      description: >-
        Returns YAML test scenarios — predefined rule + typology configurations with matching transaction samples for
        manual or automated testing.


        **Auth:** Console session (any role).
      tags:
        - Sandbox
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/blacklists:
    get:
      operationId: getBlacklists
      summary: List blacklist entries
      description: |-
        Lists blacklist entries. Expired entries (past expires_at) remain listed but are skipped during screening.

        **Auth:** Console session (any role).
      tags:
        - Blacklists
      parameters:
        - name: list
          in: query
          required: false
          description: accounts | institutions | corridors | devices | transaction_types
          schema:
            type: string
            enum:
              - accounts
              - institutions
              - corridors
              - devices
              - transaction_types
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BlacklistList"
              example:
                items: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postBlacklists
      summary: Add a blacklist entry
      description: >-
        Adds an entry. Any screening hit on a non-expired entry results in status "flagged" with maximum confidence,
        regardless of rules or model scores.


        **Auth:** Console session — analyst role required.
      tags:
        - Blacklists
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                list:
                  type: string
                  enum:
                    - accounts
                    - institutions
                    - corridors
                    - devices
                    - transaction_types
                  description: accounts | institutions | corridors | devices | transaction_types
                value:
                  type: string
                  description: The value to match (account number, BIC, "KE->NG" corridor, ...).
                reason:
                  type: string
                  description: Audit note.
                expires_at:
                  type: string
                  format: date-time
                  description: Optional expiry; entry is ignored after this time.
                min_amount:
                  type: number
                  description: Only match at or above this amount.
                corridor:
                  type: string
                  description: Restrict the entry to a corridor.
              required:
                - list
                - value
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BlacklistEntry"
              example:
                id: string
                list: accounts
                value: string
                reason: string
                source: manual
                expires_at: null
                created_at: string
                min_amount: 0
                corridor: string
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/blacklists?id={id}:
    delete:
      operationId: deleteBlacklists
      summary: Remove an entry
      description: |-
        Removes a blacklist entry by id.

        **Auth:** Console session — analyst role required.
      tags:
        - Blacklists
      parameters:
        - name: id
          in: query
          required: true
          description: Entry id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles:
    get:
      operationId: getProfiles
      summary: List behavioral profiles
      description: >-
        Lists 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.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      parameters:
        - name: risk_band
          in: query
          required: false
          description: low | medium | high
          schema:
            type: string
            enum:
              - low
              - medium
              - high
        - name: q
          in: query
          required: false
          description: Search on subject.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Page size.
          schema:
            type: number
        - name: offset
          in: query
          required: false
          description: Pagination offset.
          schema:
            type: number
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/{subject_id}:
    get:
      operationId: getProfile
      summary: Fetch a profile
      description: >-
        Full profile: derived features (typical amounts, hours, channels, corridors), risk band, embedding metadata, and
        recent_transactions[].


        **Auth:** Console session (any role).
      tags:
        - Profiles
      parameters:
        - name: subject_id
          in: path
          required: true
          description: Sender account number.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/{subject_id}/score:
    post:
      operationId: postProfileScore
      summary: What-if scoring
      description: >-
        Scores 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.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      parameters:
        - name: subject_id
          in: path
          required: true
          description: Sender account number.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                transaction:
                  type: object
                  description: "{ amount, type?, channel?, country_to?, country_from?, timestamp? }"
                receiver_account:
                  type: string
                  description: Candidate counterparty for new-counterparty detection.
              required:
                - transaction
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/{subject_id}/neighbors:
    get:
      operationId: getProfileNeighbors
      summary: Peer group
      description: |-
        Profiles most similar in embedding space — the subject’s behavioral peer group.

        **Auth:** Console session (any role).
      tags:
        - Profiles
      parameters:
        - name: subject_id
          in: path
          required: true
          description: Sender account number.
          schema:
            type: string
        - name: k
          in: query
          required: false
          description: Neighbors to return (default 5, max 20).
          schema:
            type: number
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/{subject_id}/network:
    get:
      operationId: getProfileNetwork
      summary: Counterparty network
      description: >-
        Counterparty 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.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      x-beta: true
      parameters:
        - name: subject_id
          in: path
          required: true
          description: Sender account number.
          schema:
            type: string
        - name: depth
          in: query
          required: false
          description: Traversal depth, 1–6 (default 1). Node cap 300.
          schema:
            type: number
        - name: min_txn
          in: query
          required: false
          description: Minimum transactions per edge.
          schema:
            type: number
        - name: flagged_only
          in: query
          required: false
          description: Only include flagged edges.
          schema:
            type: boolean
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/{subject_id}/trace:
    get:
      operationId: getProfileTrace
      summary: Follow-the-money trace
      description: >-
        Directed, 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.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      x-beta: true
      parameters:
        - name: subject_id
          in: path
          required: true
          description: Sender account number.
          schema:
            type: string
        - name: direction
          in: query
          required: false
          description: outbound | inbound (default outbound).
          schema:
            type: string
            enum:
              - outbound
              - inbound
        - name: max_hops
          in: query
          required: false
          description: Maximum path depth, 1–6 (default 4).
          schema:
            type: number
        - name: max_paths
          in: query
          required: false
          description: Maximum paths returned (default 25).
          schema:
            type: number
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/build:
    post:
      operationId: postProfilesBuild
      summary: Rebuild profiles
      description: >-
        Triggers an async profile (re)build over screening history. Returns a build job (202 Accepted) — poll the status
        endpoint.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/build/{job_id}:
    get:
      operationId: getProfilesBuildJob
      summary: Poll build job
      description: >-
        Polls the status of a profile build job. Returns { job: { job_id, status, subjects_processed, subjects_total,
        ... } }.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      parameters:
        - name: job_id
          in: path
          required: true
          description: Job id from POST /profiles/build.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/profiles/resolve:
    get:
      operationId: getProfilesResolve
      summary: List entity alias candidates
      description: >-
        Returns entity alias candidates for identity resolution — accounts that may belong to the same entity. Surface
        pending candidates for analyst review.


        **Auth:** Console session (any role).
      tags:
        - Profiles
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchProfilesResolve
      summary: Review an alias candidate
      description: >-
        Approves or rejects an entity alias candidate. Approved aliases merge the profiles; rejected candidates are
        dismissed.


        **Auth:** Console session — analyst role required.
      tags:
        - Profiles
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EntityAliasReview"
            example:
              id: string
              status: approved
              by: string
              note: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/networks:
    get:
      operationId: getNetworks
      summary: List extracted networks
      description: >-
        Full-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.


        **Auth:** Console session (any role).
      tags:
        - Networks
      x-beta: true
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
              example:
                overview:
                  generated_at: 2026-07-11T09:14:01Z
                  node_count: 209
                  edge_count: 160
                  networks:
                    - id: net_0111797308
                      label: Daniel Mensah network
                      node_count: 34
                      edge_count: 41
                      txn_count: 122
                      total_amount: 48120000
                      flag_rate: 0.31
                      risk_score: 0.78
                      pattern_types:
                        - fan_in
                        - pass_through
                      top_accounts:
                        - account: "0111797308"
                          name: Daniel Mensah
                          txn_count: 38
                          total_amount: 9100000
                      countries:
                        - KE
                        - TZ
                        - UG
                      institutions:
                        - KCBLKENX
                        - CRDBTZTZ
                  patterns:
                    - id: pat_fan_in_0111797308
                      type: fan_in
                      hub: "0111797308"
                      hub_name: Daniel Mensah
                      accounts:
                        - "0111797308"
                        - "0117917122"
                      score: 0.82
                      txn_count: 27
                      network_id: net_0111797308
                      detail: 9 distinct senders converge on this account within the window.
                  links:
                    - network_a: net_0111797308
                      network_b: net_0117917122
                      kind: shared_institution
                      value: KCBLKENX
                      detail: Both networks transact through KCBLKENX.
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/networks/{id}:
    get:
      operationId: getNetwork
      summary: Fetch an extracted network
      description: >-
        One 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).


        **Auth:** Console session (any role).
      tags:
        - Networks
      x-beta: true
      parameters:
        - name: id
          in: path
          required: true
          description: Network id (e.g. net_0111797308) from GET /v1/networks.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/webhooks:
    get:
      operationId: getWebhooks
      summary: List webhooks
      description: >-
        Lists registered endpoints (secrets redacted), the complete event catalog, and the 100 most recent delivery
        records.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Webhooks
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookList"
              example:
                items: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postWebhooks
      summary: Register a webhook
      description: >-
        Registers 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.


        **Auth:** Console session — admin role required.
      tags:
        - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookCreate"
            example:
              url: https://core.yourbank.example/aml/webhook
              events:
                - screening.*
                - case.status_changed
                - sar.submitted
              transport: https
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Webhook"
              example:
                webhook:
                  id: wh_9k2m1x0a
                  url: https://core.yourbank.example/aml/webhook
                  events:
                    - screening.*
                    - case.status_changed
                    - sar.submitted
                  secret: whsec_f81ka02fb4c1d9e2
                  transport: https
                  active: true
                  created_at: 2026-07-14T09:14:01Z
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchWebhooks
      summary: Update a webhook
      description: |-
        Updates the URL, subscriptions, transport, description, or active state of an endpoint.

        **Auth:** Console session — admin role required.
      tags:
        - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookUpdate"
            example:
              id: string
              url: string
              events: []
              transport: https
              active: false
              description: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/webhooks/deliveries/{id}/replay:
    post:
      operationId: replayWebhookDelivery
      summary: Replay a dead-letter delivery
      description: |-
        Re-signs the retained raw payload and resets the retry budget for a dead-letter delivery.

        **Auth:** Console session — admin role required.
      tags:
        - Webhooks
      parameters:
        - name: id
          in: path
          required: true
          description: Webhook delivery id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/webhooks?id={id}:
    delete:
      operationId: deleteWebhooks
      summary: Delete a webhook
      description: |-
        Removes a webhook endpoint. In-flight deliveries are not cancelled.

        **Auth:** Console session — admin role required.
      tags:
        - Webhooks
      parameters:
        - name: id
          in: query
          required: true
          description: Webhook id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/webhooks/alert-test:
    post:
      operationId: postWebhookAlertTest
      summary: Test the failure-notification destination
      description: >-
        Sends 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.


        **Auth:** Console session — admin role required.
      tags:
        - Webhooks
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/screen:
    post:
      operationId: postSanctionsScreen
      summary: Ad-hoc name screening
      description: >-
        Screens 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.


        **Auth:** API key — `Authorization: Bearer jc_test_...` or `X-Api-Key`. Console sessions may omit it.
      tags:
        - Sanctions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SanctionsScreenRequest"
            example:
              name: string
              names: []
              min_score: 0
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/entities:
    get:
      operationId: getSanctionsEntities
      summary: List sanctions entities
      description: |-
        Returns all watchlist entities currently loaded across all active sources.

        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/lists:
    get:
      operationId: getSanctionsLists
      summary: List watchlist sources
      description: |-
        Returns the configured watchlist sources with operational status, entity counts, and provider mode.

        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSanctionsLists
      summary: Toggle a watchlist
      description: |-
        Enables or disables a sanctions watchlist source. Disabled sources are skipped during screening.

        **Auth:** Console session — admin role required.
      tags:
        - Sanctions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SanctionsListToggle"
            example:
              source: OFAC_SDN
              enabled: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSanctionsListsRefresh
      summary: Refresh watchlists
      description: |-
        Triggers a manual refresh of all watchlist feeds. Returns the diff summary (added, updated, removed per source).

        **Auth:** Console session — analyst role required.
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/matches:
    get:
      operationId: getSanctionsMatches
      summary: List whitelisted matches
      description: >-
        Returns the false-positive whitelist — (entity, account) pairs that analysts have cleared so the match is
        suppressed on future screenings.


        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSanctionsMatches
      summary: Clear a match (false positive)
      description: >-
        Marks a sanctions match as a confirmed false positive, adding it to the whitelist so the match is suppressed on
        future screens.


        **Auth:** Console session — analyst role required.
      tags:
        - Sanctions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SanctionsMatchClear"
            example:
              entity_id: string
              account: string
              query_name: string
              reason: string
              by: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteSanctionsMatches
      summary: Re-activate a match
      description: >-
        Removes a whitelist entry, re-activating the sanctions match for future screenings. Requires the match_id query
        parameter.


        **Auth:** Console session — analyst role required.
      tags:
        - Sanctions
      parameters:
        - name: match_id
          in: query
          required: true
          description: The whitelist entry id to remove.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/ingest:
    get:
      operationId: getSanctionsIngest
      summary: Ingest freshness stats
      description: |-
        Returns current sanctions data freshness: last-updated timestamps per source and total entity count.

        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSanctionsIngest
      summary: Trigger list ingestion
      description: >-
        Triggers 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/rescreen:
    post:
      operationId: postSanctionsRescreen
      summary: Continuous re-screening
      description: >-
        Re-runs sanctions screening across the full stored history against the current lists + whitelist. Opens cases
        for counterparties that newly match after a list update.


        **Auth:** Console session — analyst role required.
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/custom-lists:
    get:
      operationId: getSanctionsCustomLists
      summary: Get client watchlist
      description: |-
        Returns the client-managed watchlist with schema, column definitions, and entity count.

        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSanctionsCustomLists
      summary: Upload client watchlist
      description: |-
        Uploads a CSV or XLSX file to replace the client-managed watchlist. Replaces the entire list atomically.

        **Auth:** Console session — admin role required.
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSanctionsCustomLists
      summary: Toggle client watchlist
      description: |-
        Enables or disables the client-managed watchlist for screening.

        **Auth:** Console session — admin role required.
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sanctions/custom-lists/template:
    get:
      operationId: getSanctionsCustomListsTemplate
      summary: Get CSV template
      description: |-
        Returns a CSV template showing the required columns for the client watchlist import.

        **Auth:** Console session (any role).
      tags:
        - Sanctions
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/metrics:
    get:
      operationId: getMetrics
      summary: Dashboard metrics
      description: |-
        Aggregated reporting metrics: totals, flag rate, top triggered rules, and 14-day trend data.

        **Auth:** Console session (any role).
      tags:
        - Reporting
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sars:
    get:
      operationId: getSars
      summary: List SAR drafts
      description: >-
        Suspicious 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.


        **Auth:** Console session (any role).
      tags:
        - Reporting
      x-beta: true
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sars/{id}/export:
    get:
      operationId: getSarExport
      summary: Export SAR draft
      description: |-
        Returns the SAR draft in a downloadable format suitable for filing with the regulator.

        **Auth:** Console session (any role).
      tags:
        - Reporting
      x-beta: true
      parameters:
        - name: id
          in: path
          required: true
          description: SAR draft id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/sars/{id}/status:
    patch:
      operationId: patchSarStatus
      summary: Advance SAR workflow
      description: >-
        Advances the SAR sign-off workflow: draft → under_review → approved → submitted. The approved/submitted
        transitions require admin role.


        **Auth:** Console session — analyst or admin role required depending on transition.
      tags:
        - Reporting
      parameters:
        - name: id
          in: path
          required: true
          description: SAR draft id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SarStatusUpdate"
            example:
              status: under_review
              by: string
              note: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/demo-requests:
    post:
      operationId: postDemoRequest
      summary: Request a demo
      description: >-
        Landing-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.


        **Auth:** Public.
      tags:
        - Onboarding
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DemoRequest"
            example:
              contactName: string
              workEmail: user@example.com
              institution: string
              country: ""
              role: ""
              deploymentInterest: managed
              volumeBand: not-sure
              message: ""
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DemoRequestResponse"
              example:
                reference: string
                status: string
                deployment_interest: managed
                nextSteps: []
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/onboarding/status:
    get:
      operationId: getOnboardingStatus
      summary: Get onboarding checklist
      description: >-
        Returns 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.


        **Auth:** Console session (any role).
      tags:
        - Onboarding
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: putOnboardingStatus
      summary: Dismiss or restore checklist
      description: >-
        Dismisses the onboarding checklist so it does not show on subsequent visits, or restores it if previously
        dismissed.


        **Auth:** Console session (any role).
      tags:
        - Onboarding
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OnboardingStatusDismiss"
            example:
              dismissed: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/settings/organization:
    get:
      operationId: getSettingsOrganization
      summary: Get organization
      description: >-
        Returns the institution profile: name, country, institution type, and active API key count. Raw API keys are
        never returned from this endpoint.


        **Auth:** Console session (any role).
      tags:
        - Settings
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSettingsOrganization
      summary: Update organization
      description: |-
        Updates organization profile fields.

        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  description: Institution name (2–80 chars).
                country:
                  type: string
                  description: ISO 3166-1 alpha-2 country code.
                institution_type:
                  type: string
                  description: bank | psp | mfi | sacco | fintech
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/settings/api-keys:
    get:
      operationId: getSettingsApiKeys
      summary: List API keys
      description: |-
        Lists the API keys with masked values, environment, and last-used timestamps.

        **Auth:** Console session (any role).
      tags:
        - Settings
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSettingsApiKeys
      summary: Create API key
      description: >-
        Sandbox 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.


        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                label:
                  type: string
                  description: Human-readable label, e.g. "Core banking integration".
                environment:
                  type: string
                  enum:
                    - test
                    - live
                  description: test | live (default test)
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
              example:
                outcome: created
                key:
                  id: key_a1b2c3d4
                  plaintext_key: jc_test_...
                  label: Core banking integration
                  environment: test
                  created_at: 2026-07-07T12:00:00.000Z
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    delete:
      operationId: deleteSettingsApiKeys
      summary: Revoke API key
      description: |-
        Revokes an API key by id. Revoked keys stop authenticating immediately.

        **Auth:** Console session — admin role required.
      tags:
        - Settings
      parameters:
        - name: id
          in: query
          required: true
          description: The key id to revoke.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSettingsApiKeys
      summary: Rotate an API key
      description: >-
        Rotates 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.


        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApiKeyRotate"
            example:
              action: string
              id: string
              grace_minutes: -9007199254740991
              label: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/settings/models:
    get:
      operationId: getSettingsModels
      summary: Get model config
      description: |-
        Returns the active ML model (mock or trained artifact) and governance settings.

        **Auth:** Console session (any role).
      tags:
        - Settings
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSettingsModels
      summary: Update model governance
      description: |-
        Updates mutable governance settings: shadow mode, drift thresholds, and challenger model toggle.

        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ModelGovernance"
            example:
              governance:
                shadow_mode: false
                drift_threshold: 0
                drift_window_days: 1
                alert_on_drift: false
                challenger_enabled: false
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/settings/retention:
    get:
      operationId: getSettingsRetention
      summary: Get retention policy
      description: >-
        Returns the current data retention policy with jurisdiction presets, category labels, and the PII field
        inventory.


        **Auth:** Console session (any role).
      tags:
        - Settings
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    patch:
      operationId: patchSettingsRetention
      summary: Update retention policy
      description: >-
        Updates the data retention policy. The jurisdiction preset and per-category retention days are validated against
        statutory minimums.


        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RetentionUpdate"
            example:
              jurisdiction_preset: string
              categories: null
              note: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/settings/invitations:
    get:
      operationId: getSettingsInvitations
      summary: List invitations
      description: |-
        Lists pending and accepted team invitations.

        **Auth:** Console session (any role).
      tags:
        - Settings
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postSettingsInvitations
      summary: Create invitation
      description: >-
        Invites 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.


        **Auth:** Console session — admin role required.
      tags:
        - Settings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvitationCreate"
            example:
              email: user@example.com
              name: string
              role: analyst
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/activity:
    get:
      operationId: getActivity
      summary: Activity / audit log
      description: >-
        Returns 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Activity
      parameters:
        - name: category
          in: query
          required: false
          description: user_action | api_request | system
          schema:
            type: string
            enum:
              - user_action
              - api_request
              - system
        - name: status
          in: query
          required: false
          description: success | denied | error
          schema:
            type: string
            enum:
              - success
              - denied
              - error
        - name: resource_type
          in: query
          required: false
          description: Filter by resource, e.g. rule, case, sanctions_match, api_key, screening.
          schema:
            type: string
        - name: q
          in: query
          required: false
          description: Free-text search across summary, action, actor, path, and resource id.
          schema:
            type: string
        - name: since
          in: query
          required: false
          description: ISO timestamp lower bound.
          schema:
            type: string
        - name: limit
          in: query
          required: false
          description: Page size (default 100, max 200).
          schema:
            type: number
        - name: offset
          in: query
          required: false
          description: Pagination offset.
          schema:
            type: number
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
              example:
                total: 1284
                items:
                  - id: act_9f2a1c7b
                    timestamp: 2026-07-08T09:41:12.004Z
                    category: user_action
                    action: sanctions.match_clear
                    actor:
                      kind: user
                      id: a.wanjiku
                      name: a.wanjiku
                    resource:
                      type: sanctions_match
                      id: pep-44120
                    summary: Cleared sanctions match for pep-44120 as false positive — DOB mismatch
                    status: success
                  - id: act_1b7d33e0
                    timestamp: 2026-07-08T09:40:58.771Z
                    category: api_request
                    action: POST
                    actor:
                      kind: api_key
                      id: key_live_2
                      name: Production key (live)
                    summary: POST /api/v1/screen → 200
                    status: success
                    method: POST
                    path: /api/v1/screen
                    status_code: 200
                    latency_ms: 42
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/health:
    get:
      operationId: getHealth
      summary: Health check
      description: >-
        Lightweight 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.


        **Auth:** Public.
      tags:
        - Meta
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/health/dependencies:
    get:
      operationId: getHealthDependencies
      summary: Dependency health detail
      description: >-
        Full 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/v1/health owns the 503 semantics.


        **Auth:** Console session — viewer role required.
      tags:
        - Meta
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/readiness:
    get:
      operationId: getReadiness
      summary: Readiness probe
      description: |-
        Readiness probe — returns 200 when the server is ready to accept traffic.

        **Auth:** Public.
      tags:
        - Meta
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/openapi:
    get:
      operationId: getOpenapi
      summary: OpenAPI 3.1 spec
      description: >-
        The 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.


        **Auth:** Public.
      tags:
        - Meta
      parameters:
        - name: format
          in: query
          required: false
          description: json — return the spec as JSON instead of YAML.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/environment:
    get:
      operationId: getEnvironment
      summary: Get console environment
      description: |-
        Returns the currently selected console environment (test or live).

        **Auth:** Console session (any role).
      tags:
        - Meta
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    put:
      operationId: putEnvironment
      summary: Switch console environment
      description: |-
        Switches the console environment between test and live. Persisted in an httpOnly cookie.

        **Auth:** Console session — analyst role required.
      tags:
        - Meta
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnvironmentUpdate"
            example:
              environment: test
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/feedback:
    get:
      operationId: getFeedback
      summary: List feedback labels
      description: |-
        Lists analyst feedback labels (true_positive, false_positive, suspicious) plus aggregated stats — newest first.

        **Auth:** Console session (any role).
      tags:
        - Feedback
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postFeedback
      summary: Label a screening
      description: >-
        Manually labels a screening record for ML training feedback. Case resolutions and SAR filings write labels
        automatically; this endpoint covers ad-hoc labeling.


        **Auth:** Console session — analyst role required.
      tags:
        - Feedback
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FeedbackCreate"
            example:
              screening_id: string
              label: true_positive
              note: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/feedback/export:
    get:
      operationId: getFeedbackExport
      summary: Export feedback labels
      description: |-
        Exports all feedback labels for ML training pipeline consumption.

        **Auth:** Console session (any role).
      tags:
        - Feedback
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/approvals:
    get:
      operationId: getApprovals
      summary: List approval requests
      description: >-
        Lists maker-checker approval requests for pending, approved, rejected, and cancelled items. Used by
        administrators to review and act on change requests.


        **Auth:** Console session — viewer role required.
      tags:
        - Approvals
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/approvals/{id}:
    get:
      operationId: getApproval
      summary: Fetch approval request
      description: |-
        Fetches a single approval request by id.

        **Auth:** Console session — viewer role required.
      tags:
        - Approvals
      parameters:
        - name: id
          in: path
          required: true
          description: Approval request id.
          schema:
            type: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postApprovalDecision
      summary: Decide an approval
      description: >-
        Approves, rejects, or cancels a pending approval request. Live API key creations and rotations require a second
        admin to approve.


        **Auth:** Console session — admin role required.
      tags:
        - Approvals
      parameters:
        - name: id
          in: path
          required: true
          description: Approval request id.
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApprovalDecision"
            example:
              decision: approve
              reason: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/approvals/sweep:
    post:
      operationId: postApprovalsSweep
      summary: Sweep for overdue approvals
      description: >-
        Scans 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 }.


        **Auth:** API key required — `Authorization: Bearer jc_live_...` or `X-Api-Key`. Machine callers only; a console
        session is not accepted.
      tags:
        - Approvals
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/backtest:
    post:
      operationId: postBacktest
      summary: Backtest a rule or typology
      description: >-
        Replays 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.


        **Auth:** Console session — analyst role required.
      tags:
        - Analytics
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BacktestRequest"
            example:
              yaml: string
              kind: rule
              window: 30d
              limit: 500
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/benchmark:
    get:
      operationId: getBenchmark
      summary: Get latest benchmark
      description: |-
        Returns the most recent benchmark result (latency percentiles and ML model card) or null if none has been run.

        **Auth:** Console session (any role).
      tags:
        - Analytics
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
    post:
      operationId: postBenchmark
      summary: Run a benchmark
      description: >-
        Runs the screening engine through N iterations to produce latency percentiles (p50, p75, p95, p99), a histogram,
        and the ML model card.


        **Auth:** Console session — analyst role required.
      tags:
        - Analytics
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BenchmarkRequest"
            example:
              n: 50
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/analytics/effectiveness:
    get:
      operationId: getAnalyticsEffectiveness
      summary: Rule / typology effectiveness
      description: >-
        Per-rule or per-typology effectiveness over time: triggers, hit rate, and precision derived from feedback
        labels. Supports 7d, 30d, 90d, and all-time windows.


        **Auth:** Console session (any role).
      tags:
        - Analytics
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /v1/invitations/accept:
    post:
      operationId: postInvitationAccept
      summary: Accept an invitation
      description: >-
        Accepts a team invitation using the token from the invite URL. Requires an authenticated session with a verified
        identity provider.


        **Auth:** Authenticated session.
      tags:
        - Account
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/InvitationAccept"
            example:
              token: string
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Missing or invalid API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "403":
          description: Insufficient role
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: jc_live_... / jc_test_...
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key
  schemas:
    Party:
      type: object
      properties:
        id:
          type: string
          description: Stable party identifier (account or wallet).
        name:
          type: string
          description: Full name (legal or registered).
        account:
          type: string
          description: Account number or wallet id.
        institution_id:
          type: string
          description: Issuing financial institution.
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code.
        kyc_level:
          type: string
          enum:
            - none
            - basic
            - standard
            - enhanced
          description: Client due-diligence tier.
        risk_score:
          description: Client-provided prior risk score (0..1).
          type: number
          minimum: 0
          maximum: 1
        pep:
          description: Politically exposed person flag.
          type: boolean
      required:
        - id
        - name
        - account
        - institution_id
        - country
        - kyc_level
      additionalProperties: false
    Transaction:
      type: object
      properties:
        id:
          type: string
          description: Instance-wide idempotency key.
        amount:
          type: number
          description: Transaction amount in minor units.
        currency:
          type: string
          description: ISO 4217 currency code.
        type:
          type: string
          enum:
            - transfer
            - card
            - cash_deposit
            - remittance
            - mobile_money
          description: Transaction type.
        channel:
          type: string
          enum:
            - api
            - branch
            - agent
            - mobile
          description: Originating channel.
        timestamp:
          type: string
          description: ISO 8601 timestamp.
        reference:
          description: External reference (e.g. source system tx id).
          type: string
        country_from:
          type: string
          description: Origin ISO 3166-1 alpha-2.
        country_to:
          type: string
          description: Destination ISO 3166-1 alpha-2.
      required:
        - id
        - amount
        - currency
        - type
        - channel
        - timestamp
        - country_from
        - country_to
      additionalProperties: false
    ScreeningRequest:
      type: object
      properties:
        transaction:
          type: object
          properties:
            id:
              type: string
              description: Instance-wide idempotency key.
            amount:
              type: number
              description: Transaction amount in minor units.
            currency:
              type: string
              description: ISO 4217 currency code.
            type:
              type: string
              enum:
                - transfer
                - card
                - cash_deposit
                - remittance
                - mobile_money
              description: Transaction type.
            channel:
              type: string
              enum:
                - api
                - branch
                - agent
                - mobile
              description: Originating channel.
            timestamp:
              type: string
              description: ISO 8601 timestamp.
            reference:
              description: External reference (e.g. source system tx id).
              type: string
            country_from:
              type: string
              description: Origin ISO 3166-1 alpha-2.
            country_to:
              type: string
              description: Destination ISO 3166-1 alpha-2.
          required:
            - id
            - amount
            - currency
            - type
            - channel
            - timestamp
            - country_from
            - country_to
          additionalProperties: false
        sender:
          type: object
          properties:
            id:
              type: string
              description: Stable party identifier (account or wallet).
            name:
              type: string
              description: Full name (legal or registered).
            account:
              type: string
              description: Account number or wallet id.
            institution_id:
              type: string
              description: Issuing financial institution.
            country:
              type: string
              description: ISO 3166-1 alpha-2 country code.
            kyc_level:
              type: string
              enum:
                - none
                - basic
                - standard
                - enhanced
              description: Client due-diligence tier.
            risk_score:
              description: Client-provided prior risk score (0..1).
              type: number
              minimum: 0
              maximum: 1
            pep:
              description: Politically exposed person flag.
              type: boolean
          required:
            - id
            - name
            - account
            - institution_id
            - country
            - kyc_level
          additionalProperties: false
        receiver:
          type: object
          properties:
            id:
              type: string
              description: Stable party identifier (account or wallet).
            name:
              type: string
              description: Full name (legal or registered).
            account:
              type: string
              description: Account number or wallet id.
            institution_id:
              type: string
              description: Issuing financial institution.
            country:
              type: string
              description: ISO 3166-1 alpha-2 country code.
            kyc_level:
              type: string
              enum:
                - none
                - basic
                - standard
                - enhanced
              description: Client due-diligence tier.
            risk_score:
              description: Client-provided prior risk score (0..1).
              type: number
              minimum: 0
              maximum: 1
            pep:
              description: Politically exposed person flag.
              type: boolean
          required:
            - id
            - name
            - account
            - institution_id
            - country
            - kyc_level
          additionalProperties: false
        metadata:
          type: object
          properties:
            device_id:
              type: string
            ip:
              type: string
            geo:
              type: string
            narrative:
              type: string
            batch_id:
              type: string
          additionalProperties: {}
      required:
        - transaction
        - sender
        - receiver
      additionalProperties: false
    ScreeningResponse:
      type: object
      properties:
        screening_id:
          type: string
        status:
          type: string
          enum:
            - cleared
            - flagged
        confidence:
          type: number
          minimum: 0
          maximum: 1
        risk_score:
          type: number
          minimum: 0
          maximum: 1
          description: Capped sum of matched rule weights.
        risk_threshold:
          type: number
          minimum: 0
          maximum: 1
          description: The institution’s configured flag threshold.
        rule_results:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              name:
                type: string
              matched:
                type: boolean
              weight:
                type: number
                minimum: 0
                maximum: 1
              severity:
                type: string
                enum:
                  - low
                  - medium
                  - high
                  - critical
              detail:
                type: string
            required:
              - rule_id
              - name
              - matched
              - weight
              - severity
            additionalProperties: false
        model_score:
          type: number
        blacklist_hits:
          type: array
          items:
            type: object
            properties:
              list:
                type: string
                enum:
                  - accounts
                  - institutions
                  - corridors
                  - devices
                  - transaction_types
              entry_id:
                type: string
              value:
                type: string
              reason:
                type: string
            required:
              - list
              - entry_id
              - value
            additionalProperties: false
        ml:
          description: Advisory ML scores; absent when scoring fails (fail-open).
          type: object
          properties:
            model_id:
              type: string
            model_version:
              type: string
            scores:
              type: object
              properties:
                anomaly_score:
                  type: number
                  minimum: 0
                  maximum: 1
                fp_likelihood:
                  type: number
                  minimum: 0
                  maximum: 1
              required:
                - anomaly_score
                - fp_likelihood
              additionalProperties: false
            explanations:
              type: array
              items:
                type: object
                properties:
                  feature:
                    type: string
                  contribution:
                    type: number
                    minimum: 0
                    maximum: 1
                  detail:
                    type: string
                required:
                  - feature
                  - contribution
                  - detail
                additionalProperties: false
            latency_ms:
              type: number
          required:
            - model_id
            - model_version
            - scores
            - explanations
            - latency_ms
          additionalProperties: false
        latency_ms:
          type: number
        timestamp:
          type: string
      required:
        - screening_id
        - status
        - confidence
        - risk_score
        - risk_threshold
        - rule_results
        - model_score
        - blacklist_hits
        - latency_ms
        - timestamp
      additionalProperties: false
    RuleResult:
      type: object
      properties:
        rule_id:
          type: string
        name:
          type: string
        matched:
          type: boolean
        weight:
          type: number
          minimum: 0
          maximum: 1
        severity:
          type: string
          enum:
            - low
            - medium
            - high
            - critical
        detail:
          type: string
      required:
        - rule_id
        - name
        - matched
        - weight
        - severity
      additionalProperties: false
    MlScoreBlock:
      type: object
      properties:
        model_id:
          type: string
        model_version:
          type: string
        scores:
          type: object
          properties:
            anomaly_score:
              type: number
              minimum: 0
              maximum: 1
            fp_likelihood:
              type: number
              minimum: 0
              maximum: 1
          required:
            - anomaly_score
            - fp_likelihood
          additionalProperties: false
        explanations:
          type: array
          items:
            type: object
            properties:
              feature:
                type: string
              contribution:
                type: number
                minimum: 0
                maximum: 1
              detail:
                type: string
            required:
              - feature
              - contribution
              - detail
            additionalProperties: false
        latency_ms:
          type: number
      required:
        - model_id
        - model_version
        - scores
        - explanations
        - latency_ms
      additionalProperties: false
    MlExplanation:
      type: object
      properties:
        feature:
          type: string
        contribution:
          type: number
          minimum: 0
          maximum: 1
        detail:
          type: string
      required:
        - feature
        - contribution
        - detail
      additionalProperties: false
    BlacklistHit:
      type: object
      properties:
        list:
          type: string
          enum:
            - accounts
            - institutions
            - corridors
            - devices
            - transaction_types
        entry_id:
          type: string
        value:
          type: string
        reason:
          type: string
      required:
        - list
        - entry_id
        - value
      additionalProperties: false
    Rule:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        severity:
          type: string
          enum:
            - low
            - medium
            - high
            - critical
        weight:
          type: number
          minimum: 0
          maximum: 1
        status:
          type: string
          enum:
            - active
            - draft
            - disabled
        version:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        yaml:
          type: string
          description: Full YAML rule definition.
        trigger_count:
          default: 0
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        hit_rate:
          default: 0
          type: number
          minimum: 0
          maximum: 1
        created_at:
          type: string
        updated_at:
          type: string
      required:
        - id
        - name
        - severity
        - weight
        - status
        - version
        - yaml
        - trigger_count
        - hit_rate
        - created_at
        - updated_at
      additionalProperties: false
    Typology:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        category:
          type: string
        rules:
          type: array
          items:
            type: object
            properties:
              rule_id:
                type: string
              weight:
                type: number
                minimum: 0
                maximum: 1
            required:
              - rule_id
              - weight
            additionalProperties: false
        alert_threshold:
          type: number
          minimum: 0
          maximum: 1
        interdict_threshold:
          type: number
          minimum: 0
          maximum: 1
        status:
          type: string
          enum:
            - active
            - draft
            - disabled
        version:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        yaml:
          type: string
          description: Full YAML typology definition.
        trigger_count:
          default: 0
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        created_at:
          type: string
        updated_at:
          type: string
      required:
        - id
        - name
        - category
        - rules
        - alert_threshold
        - status
        - version
        - yaml
        - trigger_count
        - created_at
        - updated_at
      additionalProperties: false
    Case:
      type: object
      properties:
        id:
          type: string
        environment:
          type: string
          enum:
            - test
            - live
        subject:
          type: object
          properties:
            account:
              type: string
            name:
              type: string
            institution_id:
              type: string
            country:
              type: string
            role:
              type: string
              enum:
                - sender
                - receiver
          required:
            - account
            - name
            - institution_id
            - country
            - role
          additionalProperties: false
        title:
          type: string
        status:
          type: string
          enum:
            - open
            - investigating
            - escalated
            - closed
        priority:
          type: string
          enum:
            - low
            - medium
            - high
            - critical
        source:
          type: string
          enum:
            - rule
            - typology
            - blacklist
            - sanctions
            - manual
        assignee:
          type: string
        alerts:
          type: array
          items:
            type: object
            properties:
              screening_id:
                type: string
              transaction_id:
                type: string
              timestamp:
                type: string
              status:
                type: string
                enum:
                  - cleared
                  - flagged
              amount:
                type: number
              currency:
                type: string
              matched_rule_ids:
                type: array
                items:
                  type: string
              breached_typology_ids:
                type: array
                items:
                  type: string
              sanctions_entity_ids:
                type: array
                items:
                  type: string
              max_severity:
                type: string
                enum:
                  - low
                  - medium
                  - high
                  - critical
            required:
              - screening_id
              - transaction_id
              - timestamp
              - status
              - amount
              - currency
              - matched_rule_ids
              - breached_typology_ids
              - sanctions_entity_ids
              - max_severity
            additionalProperties: false
        events:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              at:
                type: string
              actor:
                type: string
              type:
                type: string
                enum:
                  - created
                  - alert_attached
                  - status_changed
                  - priority_changed
                  - assigned
                  - note_added
                  - resolved
                  - reopened
              detail:
                type: string
            required:
              - id
              - at
              - actor
              - type
              - detail
            additionalProperties: false
        resolution:
          type: string
          enum:
            - confirmed_suspicious
            - false_positive
            - reported
            - no_action
        resolution_note:
          type: string
        sla_due_at:
          type: string
        created_at:
          type: string
        updated_at:
          type: string
        closed_at:
          type: string
      required:
        - id
        - subject
        - title
        - status
        - priority
        - source
        - alerts
        - events
        - sla_due_at
        - created_at
        - updated_at
      additionalProperties: false
    BlacklistEntry:
      type: object
      properties:
        id:
          type: string
        list:
          type: string
          enum:
            - accounts
            - institutions
            - corridors
            - devices
            - transaction_types
        value:
          type: string
        reason:
          type: string
        source:
          type: string
          enum:
            - manual
            - api
            - rule_escalated
        expires_at:
          anyOf:
            - type: string
            - type: "null"
        created_at:
          type: string
        min_amount:
          type: number
        corridor:
          type: string
      required:
        - id
        - list
        - value
        - source
        - created_at
      additionalProperties: false
    Webhook:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        transport:
          default: https
          type: string
          enum:
            - https
            - simulate
        description:
          type: string
        secret:
          type: string
        active:
          default: true
          type: boolean
        created_at:
          type: string
        updated_at:
          type: string
      required:
        - id
        - url
        - events
        - transport
        - active
        - created_at
        - updated_at
      additionalProperties: false
    ApiKey:
      type: object
      properties:
        id:
          type: string
        masked_key:
          type: string
          description: Masked display value (e.g. jct_test_a1b2****d4).
        label:
          type: string
        environment:
          type: string
          enum:
            - test
            - live
        created_at:
          type: string
        last_used_at:
          anyOf:
            - type: string
            - type: "null"
        revoked:
          default: false
          type: boolean
        expires_at:
          anyOf:
            - type: string
            - type: "null"
        replaced_by:
          anyOf:
            - type: string
            - type: "null"
      required:
        - id
        - masked_key
        - label
        - environment
        - created_at
        - revoked
      additionalProperties: false
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
          required:
            - code
            - message
          additionalProperties: {}
      required:
        - error
      additionalProperties: false
    DemoRequest:
      type: object
      properties:
        contactName:
          type: string
          minLength: 2
          maxLength: 120
          description: Full name of the person requesting the demo.
          examples:
            - Jane Muthoni
        workEmail:
          type: string
          maxLength: 254
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          description: Work email address. Personal inboxes are accepted but slow the follow-up.
          examples:
            - jane.muthoni@yourbank.example
        institution:
          type: string
          minLength: 2
          maxLength: 160
          description: Institution or company name.
          examples:
            - YourBank PLC
        country:
          default: ""
          description: Country of operation.
          examples:
            - KE
          type: string
          maxLength: 80
        role:
          default: ""
          description: Job title of the contact.
          examples:
            - Head of Compliance
          type: string
          maxLength: 120
        deploymentInterest:
          type: string
          enum:
            - managed
            - on-premise
            - undecided
          description: Managed service, on-premise deployment, or undecided.
          examples:
            - managed
        volumeBand:
          default: not-sure
          description: Estimated monthly screening volume.
          examples:
            - 100k-2m
          type: string
          enum:
            - under-100k
            - 100k-2m
            - 2m-50m
            - 50m-500m
            - over-500m
            - not-sure
        message:
          default: ""
          description: Anything specific the prospect wants covered in the demo.
          type: string
          maxLength: 2000
      required:
        - contactName
        - workEmail
        - institution
        - country
        - role
        - deploymentInterest
        - volumeBand
        - message
      additionalProperties: false
    DemoRequestResponse:
      type: object
      properties:
        reference:
          type: string
          description: Demo request reference code (e.g. DEMO-...).
        status:
          type: string
          const: received
          description: Submission status.
        deployment_interest:
          type: string
          enum:
            - managed
            - on-premise
            - undecided
          description: Echoed deployment interest.
        nextSteps:
          type: array
          items:
            type: string
          description: What happens next.
      required:
        - reference
        - status
        - deployment_interest
        - nextSteps
      additionalProperties: false
    CaseList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              environment:
                type: string
                enum:
                  - test
                  - live
              subject:
                type: object
                properties:
                  account:
                    type: string
                  name:
                    type: string
                  institution_id:
                    type: string
                  country:
                    type: string
                  role:
                    type: string
                    enum:
                      - sender
                      - receiver
                required:
                  - account
                  - name
                  - institution_id
                  - country
                  - role
                additionalProperties: false
              title:
                type: string
              status:
                type: string
                enum:
                  - open
                  - investigating
                  - escalated
                  - closed
              priority:
                type: string
                enum:
                  - low
                  - medium
                  - high
                  - critical
              source:
                type: string
                enum:
                  - rule
                  - typology
                  - blacklist
                  - sanctions
                  - manual
              assignee:
                type: string
              alerts:
                type: array
                items:
                  type: object
                  properties:
                    screening_id:
                      type: string
                    transaction_id:
                      type: string
                    timestamp:
                      type: string
                    status:
                      type: string
                      enum:
                        - cleared
                        - flagged
                    amount:
                      type: number
                    currency:
                      type: string
                    matched_rule_ids:
                      type: array
                      items:
                        type: string
                    breached_typology_ids:
                      type: array
                      items:
                        type: string
                    sanctions_entity_ids:
                      type: array
                      items:
                        type: string
                    max_severity:
                      type: string
                      enum:
                        - low
                        - medium
                        - high
                        - critical
                  required:
                    - screening_id
                    - transaction_id
                    - timestamp
                    - status
                    - amount
                    - currency
                    - matched_rule_ids
                    - breached_typology_ids
                    - sanctions_entity_ids
                    - max_severity
                  additionalProperties: false
              events:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                    at:
                      type: string
                    actor:
                      type: string
                    type:
                      type: string
                      enum:
                        - created
                        - alert_attached
                        - status_changed
                        - priority_changed
                        - assigned
                        - note_added
                        - resolved
                        - reopened
                    detail:
                      type: string
                  required:
                    - id
                    - at
                    - actor
                    - type
                    - detail
                  additionalProperties: false
              resolution:
                type: string
                enum:
                  - confirmed_suspicious
                  - false_positive
                  - reported
                  - no_action
              resolution_note:
                type: string
              sla_due_at:
                type: string
              created_at:
                type: string
              updated_at:
                type: string
              closed_at:
                type: string
            required:
              - id
              - subject
              - title
              - status
              - priority
              - source
              - alerts
              - events
              - sla_due_at
              - created_at
              - updated_at
            additionalProperties: false
        total:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        stats:
          type: object
          properties:
            open:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            investigating:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            escalated:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            closed:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
          required:
            - open
            - investigating
            - escalated
            - closed
          additionalProperties: false
        institutions:
          type: array
          items:
            type: string
        assignees:
          type: array
          items:
            type: string
      required:
        - items
        - total
        - stats
        - institutions
        - assignees
      additionalProperties: false
    RuleList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              description:
                type: string
              severity:
                type: string
                enum:
                  - low
                  - medium
                  - high
                  - critical
              weight:
                type: number
                minimum: 0
                maximum: 1
              status:
                type: string
                enum:
                  - active
                  - draft
                  - disabled
              version:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              yaml:
                type: string
                description: Full YAML rule definition.
              trigger_count:
                default: 0
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              hit_rate:
                default: 0
                type: number
                minimum: 0
                maximum: 1
              created_at:
                type: string
              updated_at:
                type: string
            required:
              - id
              - name
              - severity
              - weight
              - status
              - version
              - yaml
              - trigger_count
              - hit_rate
              - created_at
              - updated_at
            additionalProperties: false
      required:
        - items
      additionalProperties: false
    TypologyList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              description:
                type: string
              category:
                type: string
              rules:
                type: array
                items:
                  type: object
                  properties:
                    rule_id:
                      type: string
                    weight:
                      type: number
                      minimum: 0
                      maximum: 1
                  required:
                    - rule_id
                    - weight
                  additionalProperties: false
              alert_threshold:
                type: number
                minimum: 0
                maximum: 1
              interdict_threshold:
                type: number
                minimum: 0
                maximum: 1
              status:
                type: string
                enum:
                  - active
                  - draft
                  - disabled
              version:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              yaml:
                type: string
                description: Full YAML typology definition.
              trigger_count:
                default: 0
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              created_at:
                type: string
              updated_at:
                type: string
            required:
              - id
              - name
              - category
              - rules
              - alert_threshold
              - status
              - version
              - yaml
              - trigger_count
              - created_at
              - updated_at
            additionalProperties: false
      required:
        - items
      additionalProperties: false
    BlacklistList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              list:
                type: string
                enum:
                  - accounts
                  - institutions
                  - corridors
                  - devices
                  - transaction_types
              value:
                type: string
              reason:
                type: string
              source:
                type: string
                enum:
                  - manual
                  - api
                  - rule_escalated
              expires_at:
                anyOf:
                  - type: string
                  - type: "null"
              created_at:
                type: string
              min_amount:
                type: number
              corridor:
                type: string
            required:
              - id
              - list
              - value
              - source
              - created_at
            additionalProperties: false
      required:
        - items
      additionalProperties: false
    WebhookList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              url:
                type: string
                format: uri
              events:
                type: array
                items:
                  type: string
              transport:
                default: https
                type: string
                enum:
                  - https
                  - simulate
              description:
                type: string
              secret:
                type: string
              active:
                default: true
                type: boolean
              created_at:
                type: string
              updated_at:
                type: string
            required:
              - id
              - url
              - events
              - transport
              - active
              - created_at
              - updated_at
            additionalProperties: false
      required:
        - items
      additionalProperties: false
    OrganizationSettings:
      type: object
      properties:
        organization:
          type: object
          properties:
            name:
              type: string
              description: Institution name.
            country:
              type: string
              description: ISO 3166-1 alpha-2 country code.
            institution_type:
              type: string
              description: Institution type.
            risk_threshold:
              type: number
              minimum: 0
              maximum: 1
              description: The institution’s configured flag threshold (0..1).
            created_at:
              type: string
              description: Institution record creation timestamp.
            api_key_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
              description: Count of non-revoked API keys.
            pinned_fields:
              type: array
              items:
                type: string
                enum:
                  - name
                  - country
                  - institution_type
                  - risk_threshold
              description: "Fields set by this deployment’s configuration. Read-only: a PATCH touching one returns 409."
          required:
            - name
            - country
            - institution_type
            - risk_threshold
            - created_at
            - api_key_count
            - pinned_fields
          additionalProperties: false
          description: Organization settings.
      required:
        - organization
      additionalProperties: false
    OrganizationUpdate:
      type: object
      properties:
        name:
          description: Institution name.
          type: string
          minLength: 2
          maxLength: 80
        country:
          description: ISO 3166-1 alpha-2 country code.
          type: string
          pattern: ^[A-Z]{2}$
        institution_type:
          description: Institution type.
          type: string
          maxLength: 32
        risk_threshold:
          description: The institution’s configured flag threshold (0..1). Changes go through maker-checker approval.
          type: number
          minimum: 0
          maximum: 1
        webhook_alert_url:
          description: HTTPS destination for webhook delivery-failure notifications (e.g. a Slack incoming webhook). Send an empty
            string to clear it. Never returned in full — reads give a masked hint.
          type: string
          maxLength: 2048
      additionalProperties: false
    ApiKeyList:
      type: object
      properties:
        keys:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              masked_key:
                type: string
              label:
                type: string
              environment:
                type: string
                enum:
                  - test
                  - live
              created_at:
                type: string
              last_used_at:
                anyOf:
                  - type: string
                  - type: "null"
              revoked:
                default: false
                type: boolean
              expires_at:
                anyOf:
                  - type: string
                  - type: "null"
              replaced_by:
                anyOf:
                  - type: string
                  - type: "null"
            required:
              - id
              - masked_key
              - label
              - environment
              - created_at
              - revoked
            additionalProperties: false
      required:
        - keys
      additionalProperties: false
    ApiKeyCreate:
      type: object
      properties:
        label:
          description: Human-readable label.
          type: string
          maxLength: 48
        environment:
          default: test
          description: API key environment.
          type: string
          enum:
            - test
            - live
      required:
        - environment
      additionalProperties: false
    ApiKeyRotate:
      type: object
      properties:
        action:
          type: string
          const: rotate
        id:
          type: string
          description: Key id to rotate.
        grace_minutes:
          description: Old key grace period in minutes.
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        label:
          description: New key label.
          type: string
          maxLength: 48
      required:
        - action
        - id
      additionalProperties: false
    InvitationCreate:
      type: object
      properties:
        email:
          type: string
          format: email
          pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
          description: Invitee email address.
        name:
          type: string
          minLength: 1
          maxLength: 120
          description: Invitee full name.
        role:
          default: analyst
          description: Assigned role.
          type: string
          enum:
            - admin
            - compliance_officer
            - analyst
            - viewer
      required:
        - email
        - name
        - role
      additionalProperties: false
    ModelGovernance:
      type: object
      properties:
        governance:
          type: object
          properties:
            shadow_mode:
              type: boolean
            drift_threshold:
              type: number
              minimum: 0
              maximum: 1
            drift_window_days:
              type: integer
              minimum: 1
              maximum: 90
            alert_on_drift:
              type: boolean
            challenger_enabled:
              type: boolean
          additionalProperties: false
      required:
        - governance
      additionalProperties: false
    ProfileList:
      type: object
      properties:
        profiles:
          type: array
          items:
            type: object
            properties:
              subject_id:
                type: string
              risk_band:
                type: string
                enum:
                  - low
                  - medium
                  - high
              anomaly_score:
                type: number
                minimum: 0
                maximum: 1
              maturity:
                type: string
              confidence:
                type: number
                minimum: 0
                maximum: 1
              txn_count:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              features:
                type: object
                propertyNames:
                  type: string
                additionalProperties: {}
              created_at:
                type: string
              updated_at:
                type: string
            required:
              - subject_id
              - risk_band
              - anomaly_score
              - maturity
              - confidence
              - txn_count
              - features
              - created_at
              - updated_at
            additionalProperties: false
        total:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        next_cursor:
          anyOf:
            - type: string
            - type: "null"
        has_more:
          type: boolean
        built_at:
          anyOf:
            - type: string
            - type: "null"
        embedding_model:
          type: string
      required:
        - profiles
        - total
        - next_cursor
        - has_more
        - built_at
        - embedding_model
      additionalProperties: false
    ProfileDetail:
      type: object
      properties:
        profile:
          type: object
          properties:
            subject_id:
              type: string
            risk_band:
              type: string
              enum:
                - low
                - medium
                - high
            anomaly_score:
              type: number
              minimum: 0
              maximum: 1
            maturity:
              type: string
            confidence:
              type: number
              minimum: 0
              maximum: 1
            txn_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            features:
              type: object
              propertyNames:
                type: string
              additionalProperties: {}
            created_at:
              type: string
            updated_at:
              type: string
          required:
            - subject_id
            - risk_band
            - anomaly_score
            - maturity
            - confidence
            - txn_count
            - features
            - created_at
            - updated_at
          additionalProperties: false
        recent_transactions:
          type: array
          items:
            type: object
            properties:
              screening_id:
                type: string
              transaction_id:
                type: string
              amount:
                type: number
              currency:
                type: string
              status:
                type: string
              timestamp:
                type: string
            required:
              - screening_id
              - amount
              - currency
              - status
              - timestamp
            additionalProperties: false
      required:
        - profile
        - recent_transactions
      additionalProperties: false
    ProfileScoreRequest:
      type: object
      properties:
        transaction:
          type: object
          properties:
            amount:
              type: number
              exclusiveMinimum: 0
            type:
              type: string
            channel:
              type: string
            country_to:
              type: string
            country_from:
              type: string
            timestamp:
              type: string
          required:
            - amount
          additionalProperties: false
        receiver_account:
          type: string
      required:
        - transaction
      additionalProperties: false
    ProfileNeighbors:
      type: object
      properties:
        neighbors:
          type: array
          items:
            type: object
            properties:
              subject_id:
                type: string
              similarity:
                type: number
                minimum: 0
                maximum: 1
              risk_band:
                type: string
                enum:
                  - low
                  - medium
                  - high
              txn_count:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
            required:
              - subject_id
              - similarity
              - risk_band
              - txn_count
            additionalProperties: false
      required:
        - neighbors
      additionalProperties: false
    ProfileNetwork:
      type: object
      properties:
        network:
          type: object
          properties:
            root:
              type: string
            nodes:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  depth:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  risk_band:
                    type: string
                  blacklisted:
                    type: boolean
                  txn_count:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                required:
                  - id
                  - depth
                  - risk_band
                  - blacklisted
                  - txn_count
                additionalProperties: false
            edges:
              type: array
              items:
                type: object
                properties:
                  source:
                    type: string
                  target:
                    type: string
                  txn_count:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  total_amount:
                    type: number
                  flagged_count:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                  corridors:
                    type: array
                    items:
                      type: string
                required:
                  - source
                  - target
                  - txn_count
                  - total_amount
                  - flagged_count
                  - corridors
                additionalProperties: false
            truncated:
              type: boolean
          required:
            - root
            - nodes
            - edges
            - truncated
          additionalProperties: false
      required:
        - network
      additionalProperties: false
    ProfileTrace:
      type: object
      properties:
        trace:
          type: object
          properties:
            root:
              type: string
            direction:
              type: string
              enum:
                - outbound
                - inbound
            paths:
              type: array
              items:
                type: object
                properties:
                  hops:
                    type: array
                    items:
                      type: object
                      properties:
                        account:
                          type: string
                        amount:
                          type: number
                        currency:
                          type: string
                        timestamp:
                          type: string
                      required:
                        - account
                        - amount
                        - currency
                        - timestamp
                      additionalProperties: false
                  bottleneck_volume:
                    type: number
                  suspicious:
                    type: boolean
                  length:
                    type: integer
                    minimum: -9007199254740991
                    maximum: 9007199254740991
                required:
                  - hops
                  - bottleneck_volume
                  - suspicious
                  - length
                additionalProperties: false
            truncated:
              type: boolean
          required:
            - root
            - direction
            - paths
            - truncated
          additionalProperties: false
      required:
        - trace
      additionalProperties: false
    ProfileBuildJob:
      type: object
      properties:
        job:
          type: object
          properties:
            job_id:
              type: string
            status:
              type: string
            started_at:
              type: string
            completed_at:
              type: string
            subjects_processed:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            subjects_total:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
          required:
            - job_id
            - status
          additionalProperties: false
      required:
        - job
      additionalProperties: false
    SandboxRunRequest:
      type: object
      properties:
        transactions:
          type: array
          items:
            type: object
            properties: {}
            additionalProperties: {}
        run_scenarios:
          type: boolean
        scenario_ids:
          type: array
          items:
            type: string
        rule_ids:
          type: array
          items:
            type: string
        include_drafts:
          type: boolean
        yaml:
          type: string
        yaml_only:
          type: boolean
        typology_yaml:
          type: string
        typology_only:
          type: boolean
      additionalProperties: false
    SandboxRunResponse:
      type: object
      properties:
        run_id:
          type: string
        started_at:
          type: string
        duration_ms:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        summary:
          type: object
          properties:
            total:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            cleared:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            flagged:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            avg_confidence:
              type: number
              minimum: 0
              maximum: 1
          required:
            - total
            - cleared
            - flagged
            - avg_confidence
          additionalProperties: false
        results:
          type: array
          items:
            type: object
            properties:
              request:
                type: object
                properties: {}
                additionalProperties: {}
              response:
                type: object
                properties: {}
                additionalProperties: {}
            required:
              - request
              - response
            additionalProperties: false
      required:
        - run_id
        - started_at
        - duration_ms
        - summary
        - results
      additionalProperties: false
    SandboxSample:
      type: object
      properties:
        transactions:
          type: array
          items:
            type: object
            properties: {}
            additionalProperties: {}
      required:
        - transactions
      additionalProperties: false
    TransactionList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties: {}
            additionalProperties: {}
        total:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        limit:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        offset:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        next_cursor:
          anyOf:
            - type: string
            - type: "null"
        has_more:
          type: boolean
        counts:
          type: object
          properties:
            all:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            cleared:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            flagged:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
          required:
            - all
            - cleared
            - flagged
          additionalProperties: false
      required:
        - items
        - total
        - limit
        - offset
        - next_cursor
        - has_more
        - counts
      additionalProperties: false
    TransactionPatch:
      type: object
      properties:
        screening_id:
          type: string
        reviewed:
          type: boolean
        disposition:
          type: string
          enum:
            - true_positive
            - false_positive
            - escalated
        note:
          type: string
      required:
        - screening_id
      additionalProperties: false
    TransactionImport:
      type: object
      properties:
        raw:
          type: string
          minLength: 1
        filename:
          type: string
        dry_run:
          type: boolean
      required:
        - raw
      additionalProperties: false
    NetworkOverview:
      type: object
      properties:
        overview:
          type: object
          properties:
            generated_at:
              type: string
            node_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            edge_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            networks:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
            patterns:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
            links:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
          required:
            - generated_at
            - node_count
            - edge_count
            - networks
            - patterns
            - links
          additionalProperties: false
      required:
        - overview
      additionalProperties: false
    NetworkDetail:
      type: object
      properties:
        network:
          type: object
          properties:
            id:
              type: string
            label:
              type: string
            node_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            edge_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            txn_count:
              type: integer
              minimum: -9007199254740991
              maximum: 9007199254740991
            total_amount:
              type: number
            flag_rate:
              type: number
              minimum: 0
              maximum: 1
            risk_score:
              type: number
              minimum: 0
              maximum: 1
            pattern_types:
              type: array
              items:
                type: string
            top_accounts:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
            nodes:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
            edges:
              type: array
              items:
                type: object
                properties: {}
                additionalProperties: {}
          required:
            - id
            - node_count
            - edge_count
            - txn_count
            - total_amount
            - flag_rate
            - risk_score
            - pattern_types
            - top_accounts
            - nodes
            - edges
          additionalProperties: false
      required:
        - network
      additionalProperties: false
    ActivityList:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              timestamp:
                type: string
              category:
                type: string
              action:
                type: string
              actor:
                type: object
                properties:
                  kind:
                    type: string
                  id:
                    type: string
                  name:
                    type: string
                required:
                  - kind
                  - id
                additionalProperties: false
              resource:
                type: object
                properties:
                  type:
                    type: string
                  id:
                    type: string
                required:
                  - type
                  - id
                additionalProperties: false
              summary:
                type: string
              status:
                type: string
              method:
                type: string
              path:
                type: string
              status_code:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              latency_ms:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
            required:
              - id
              - timestamp
              - category
              - action
              - actor
              - summary
              - status
            additionalProperties: false
        total:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
      required:
        - items
        - total
      additionalProperties: false
    DashboardMetrics:
      type: object
      properties:
        total_transactions:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        flagged:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        cleared:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        flagged_rate:
          type: number
          minimum: 0
          maximum: 1
        by_type:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: integer
            minimum: -9007199254740991
            maximum: 9007199254740991
        by_channel:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: integer
            minimum: -9007199254740991
            maximum: 9007199254740991
        by_currency:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: integer
            minimum: -9007199254740991
            maximum: 9007199254740991
        rule_effectiveness:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: object
            properties: {}
            additionalProperties: {}
        risk_distribution:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: integer
            minimum: -9007199254740991
            maximum: 9007199254740991
        environment:
          type: string
      required:
        - total_transactions
        - flagged
        - cleared
        - flagged_rate
        - by_type
        - by_channel
        - by_currency
        - rule_effectiveness
        - risk_distribution
        - environment
      additionalProperties: false
    SarList:
      type: object
      properties:
        sars:
          type: array
          items:
            type: object
            properties:
              sar_id:
                type: string
              screening_id:
                type: string
              status:
                type: string
              created_at:
                type: string
              escalated_by:
                type: string
              subject:
                type: object
                properties:
                  name:
                    type: string
                  account:
                    type: string
                  institution_id:
                    type: string
                  country:
                    type: string
                required:
                  - name
                  - account
                  - institution_id
                  - country
                additionalProperties: false
              counterparty:
                type: object
                properties:
                  name:
                    type: string
                  account:
                    type: string
                  institution_id:
                    type: string
                  country:
                    type: string
                required:
                  - name
                  - account
                  - institution_id
                  - country
                additionalProperties: false
              transaction:
                type: object
                properties:
                  id:
                    type: string
                  amount:
                    type: number
                  currency:
                    type: string
                  type:
                    type: string
                  corridor:
                    type: string
                  timestamp:
                    type: string
                required:
                  - id
                  - amount
                  - currency
                  - type
                  - corridor
                  - timestamp
                additionalProperties: false
              matched_rules:
                type: array
                items:
                  type: object
                  properties:
                    rule_id:
                      type: string
                    name:
                      type: string
                    severity:
                      type: string
                  required:
                    - rule_id
                    - name
                    - severity
                  additionalProperties: false
              narrative:
                type: string
            required:
              - sar_id
              - screening_id
              - status
              - created_at
              - subject
              - counterparty
              - transaction
              - matched_rules
              - narrative
            additionalProperties: false
        total:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
      required:
        - sars
        - total
      additionalProperties: false
    WebhookCreate:
      type: object
      properties:
        url:
          type: string
          format: uri
        events:
          minItems: 1
          type: array
          items:
            type: string
        transport:
          default: https
          type: string
          enum:
            - https
            - simulate
        description:
          type: string
          maxLength: 120
      required:
        - url
        - events
        - transport
      additionalProperties: false
    WebhookUpdate:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        events:
          minItems: 1
          type: array
          items:
            type: string
        transport:
          type: string
          enum:
            - https
            - simulate
        active:
          type: boolean
        description:
          type: string
          maxLength: 120
      required:
        - id
      additionalProperties: false
    BacktestRequest:
      type: object
      properties:
        yaml:
          type: string
          minLength: 1
        kind:
          default: rule
          type: string
          enum:
            - rule
            - typology
        window:
          default: 30d
          type: string
          enum:
            - 7d
            - 30d
            - 90d
            - all
        limit:
          default: 500
          type: integer
          minimum: -9007199254740991
          maximum: 2000
      required:
        - yaml
        - kind
        - window
        - limit
      additionalProperties: false
    Health:
      type: object
      properties:
        status:
          type: string
          enum:
            - ok
            - degraded
        storage:
          type: string
          enum:
            - mock
            - database
        db:
          type: string
          enum:
            - not-configured
            - ok
            - error
        rules:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
      required:
        - status
        - storage
        - db
      additionalProperties: false
    Environment:
      type: object
      properties:
        environment:
          type: string
          enum:
            - test
            - live
      required:
        - environment
      additionalProperties: false
    EnvironmentUpdate:
      type: object
      properties:
        environment:
          type: string
          enum:
            - test
            - live
      required:
        - environment
      additionalProperties: false
    ScreeningIso20022Request:
      type: object
      properties:
        Document:
          type: object
          properties:
            FIToFICstmrCdtTrf:
              type: object
              properties:
                GrpHdr:
                  type: object
                  properties:
                    MsgId:
                      type: string
                      description: Unique message identifier
                    CreDtTm:
                      type: string
                      description: Creation date and time
                    NbOfTxs:
                      type: string
                      description: Number of transactions
                  required:
                    - MsgId
                    - CreDtTm
                    - NbOfTxs
                  additionalProperties: false
                  description: Group header
                CdtTrfTxInf:
                  type: object
                  properties:
                    PmtId:
                      type: object
                      properties:
                        EndToEndId:
                          description: End-to-end identifier
                          type: string
                        TxId:
                          description: Transaction identifier
                          type: string
                        UETR:
                          description: Universal end-to-end transaction reference
                          type: string
                      additionalProperties: false
                      description: Payment identification
                    IntrBkSttlmAmt:
                      type: object
                      properties:
                        Ccy:
                          type: string
                          description: Currency code
                        value:
                          type: string
                          description: Settlement amount
                      required:
                        - Ccy
                        - value
                      additionalProperties: false
                      description: Interbank settlement amount
                    IntrBkSttlmDt:
                      description: Interbank settlement date
                      type: string
                    Dbtr:
                      type: object
                      properties:
                        Nm:
                          type: string
                          description: Debtor name
                      required:
                        - Nm
                      additionalProperties: false
                      description: Debtor
                    DbtrAcct:
                      type: object
                      properties:
                        Id:
                          type: object
                          properties:
                            Othr:
                              type: object
                              properties:
                                Id:
                                  type: string
                                  description: Debtor account identifier
                              required:
                                - Id
                              additionalProperties: false
                          required:
                            - Othr
                          additionalProperties: false
                      required:
                        - Id
                      additionalProperties: false
                      description: Debtor account
                    DbtrAgt:
                      type: object
                      properties:
                        FinInstnId:
                          type: object
                          properties:
                            BICFI:
                              type: string
                              description: Debtor agent BIC
                          required:
                            - BICFI
                          additionalProperties: false
                      required:
                        - FinInstnId
                      additionalProperties: false
                      description: Debtor agent
                    Cdtr:
                      type: object
                      properties:
                        Nm:
                          type: string
                          description: Creditor name
                      required:
                        - Nm
                      additionalProperties: false
                      description: Creditor
                    CdtrAcct:
                      type: object
                      properties:
                        Id:
                          type: object
                          properties:
                            Othr:
                              type: object
                              properties:
                                Id:
                                  type: string
                                  description: Creditor account identifier
                              required:
                                - Id
                              additionalProperties: false
                          required:
                            - Othr
                          additionalProperties: false
                      required:
                        - Id
                      additionalProperties: false
                      description: Creditor account
                    CdtrAgt:
                      type: object
                      properties:
                        FinInstnId:
                          type: object
                          properties:
                            BICFI:
                              type: string
                              description: Creditor agent BIC
                          required:
                            - BICFI
                          additionalProperties: false
                      required:
                        - FinInstnId
                      additionalProperties: false
                      description: Creditor agent
                    RmtInf:
                      description: Remittance information
                      type: object
                      properties:
                        Ustrd:
                          description: Unstructured remittance information
                          type: array
                          items:
                            type: string
                      additionalProperties: false
                  required:
                    - PmtId
                    - IntrBkSttlmAmt
                    - Dbtr
                    - DbtrAcct
                    - DbtrAgt
                    - Cdtr
                    - CdtrAcct
                    - CdtrAgt
                  additionalProperties: false
                  description: Credit transfer transaction information
              required:
                - GrpHdr
                - CdtTrfTxInf
              additionalProperties: false
          required:
            - FIToFICstmrCdtTrf
          additionalProperties: false
          description: ISO 20022 FIToFICstmrCdtTrf document
      required:
        - Document
      additionalProperties: false
      description: ISO 20022 pacs.008 screening request
    ScreeningMojaloopRequest:
      type: object
      properties:
        transactionId:
          type: string
          description: Mojaloop transaction identifier
        quoteId:
          description: Quote request identifier
          type: string
        amount:
          type: object
          properties:
            amount:
              type: string
              description: Transfer amount
            currency:
              type: string
              description: Currency code
          required:
            - amount
            - currency
          additionalProperties: false
          description: Transfer amount
        transactionType:
          description: Transaction type details
          type: object
          properties:
            scenario:
              type: string
              description: Transaction scenario (e.g. TRANSFER)
            initiator:
              type: string
              description: Transaction initiator (e.g. PAYER)
            initiatorType:
              type: string
              description: Initiator type (e.g. CONSUMER)
          required:
            - scenario
            - initiator
            - initiatorType
          additionalProperties: false
        payer:
          type: object
          properties:
            partyIdInfo:
              type: object
              properties:
                partyIdType:
                  type: string
                  description: Party identifier type (e.g. MSISDN)
                partyIdentifier:
                  type: string
                  description: Party identifier value
                fspId:
                  type: string
                  description: Financial service provider ID
              required:
                - partyIdType
                - partyIdentifier
                - fspId
              additionalProperties: false
              description: Payer party identifier info
            name:
              description: Payer display name
              type: string
            personalInfo:
              description: Payer personal information
              type: object
              properties:
                givenName:
                  type: string
                familyName:
                  type: string
                dateOfBirth:
                  type: string
                placeOfBirth:
                  type: string
                nationality:
                  type: string
              additionalProperties: false
          required:
            - partyIdInfo
          additionalProperties: false
          description: Payer party
        payee:
          type: object
          properties:
            partyIdInfo:
              type: object
              properties:
                partyIdType:
                  type: string
                  description: Party identifier type (e.g. MSISDN)
                partyIdentifier:
                  type: string
                  description: Party identifier value
                fspId:
                  type: string
                  description: Financial service provider ID
              required:
                - partyIdType
                - partyIdentifier
                - fspId
              additionalProperties: false
              description: Payee party identifier info
            name:
              description: Payee display name
              type: string
            personalInfo:
              description: Payee personal information
              type: object
              properties:
                givenName:
                  type: string
                familyName:
                  type: string
                dateOfBirth:
                  type: string
                placeOfBirth:
                  type: string
                nationality:
                  type: string
              additionalProperties: false
          required:
            - partyIdInfo
          additionalProperties: false
          description: Payee party
        note:
          description: Free-text note mapped to metadata.narrative
          type: string
      required:
        - transactionId
        - amount
        - payer
        - payee
      additionalProperties: false
      description: Mojaloop transaction screening request
    OnboardingStatusDismiss:
      type: object
      properties:
        dismissed:
          type: boolean
          description: True to dismiss the checklist, false to restore it
      required:
        - dismissed
      additionalProperties: false
      description: Onboarding checklist dismiss/restore request
    FeedbackCreate:
      type: object
      properties:
        screening_id:
          type: string
          description: Screening record to label
        label:
          type: string
          enum:
            - true_positive
            - false_positive
            - suspicious
          description: Analyst feedback label
        note:
          description: Optional analyst note
          type: string
          maxLength: 500
      required:
        - screening_id
        - label
      additionalProperties: false
      description: Feedback label creation request
    ApprovalDecision:
      type: object
      properties:
        decision:
          type: string
          enum:
            - approve
            - reject
            - cancel
          description: Approval decision
        reason:
          description: Rationale for the decision
          type: string
          maxLength: 500
      required:
        - decision
      additionalProperties: false
      description: Approval decision request
    SarStatusUpdate:
      type: object
      properties:
        status:
          type: string
          enum:
            - under_review
            - approved
            - submitted
          description: Target SAR workflow status
        by:
          description: Reviewer/approver name
          type: string
        note:
          description: Optional note for the status transition
          type: string
      required:
        - status
      additionalProperties: false
      description: SAR workflow status update request
    SanctionsScreenRequest:
      type: object
      properties:
        name:
          description: Single name to screen
          type: string
        names:
          description: Multiple names to screen (alternative to name)
          type: array
          items:
            type: string
        min_score:
          description: Minimum score threshold to surface candidates
          type: number
          minimum: 0
          maximum: 1
      additionalProperties: false
      description: Sanctions name screening request
    SanctionsListToggle:
      type: object
      properties:
        source:
          type: string
          enum:
            - OFAC_SDN
            - UN_CONSOLIDATED
            - EU_FSF
            - UK_HMT
            - OPENSANCTIONS_PEP
            - ADVERSE_MEDIA
            - CUSTOM_CLIENT
          description: Sanctions source identifier
        enabled:
          type: boolean
          description: Enable or disable the watchlist
      required:
        - source
        - enabled
      additionalProperties: false
      description: Sanctions list enable/disable request
    SanctionsMatchClear:
      type: object
      properties:
        entity_id:
          type: string
          description: Sanctions entity identifier
        account:
          type: string
          description: Account number to clear
        query_name:
          description: Name used in the original query
          type: string
        reason:
          description: Reason for clearing the match
          type: string
        by:
          description: Analyst who cleared the match
          type: string
      required:
        - entity_id
        - account
      additionalProperties: false
      description: Sanctions false-positive clearance request
    EntityAliasReview:
      type: object
      properties:
        id:
          type: string
          description: Entity alias identifier to review
        status:
          type: string
          enum:
            - approved
            - rejected
          description: Review decision
        by:
          description: Reviewer name
          type: string
        note:
          description: Optional review note
          type: string
      required:
        - id
        - status
      additionalProperties: false
      description: Entity alias review request
    InvitationAccept:
      type: object
      properties:
        token:
          type: string
          description: Invitation token from the invite URL
      required:
        - token
      additionalProperties: false
      description: Invitation acceptance request
    RetentionUpdate:
      type: object
      properties:
        jurisdiction_preset:
          type: string
          description: Jurisdiction preset identifier
        categories:
          type: object
          propertyNames:
            type: string
          additionalProperties:
            type: object
            properties:
              retention_days:
                type: integer
                minimum: -9007199254740991
                maximum: 9007199254740991
              expiry_action:
                type: string
                enum:
                  - purge
                  - anonymise
            required:
              - retention_days
              - expiry_action
            additionalProperties: false
        note:
          description: Change rationale
          type: string
          maxLength: 500
      required:
        - jurisdiction_preset
        - categories
      additionalProperties: false
      description: Data retention policy update request
    BenchmarkRequest:
      type: object
      properties:
        n:
          default: 50
          description: Number of benchmark iterations (capped at 200)
          type: integer
          minimum: 1
          maximum: 200
      required:
        - n
      additionalProperties: false
      description: Benchmark run request
