{
  "openapi": "3.1.0",
  "info": {
    "title": "KulPay \u2014 Public API",
    "version": "1.0.0",
    "summary": "Payment gateway for Mozambican and international payment rails.",
    "description": "KulPay is a payment gateway for Mozambique: mobile money (M-Pesa, eMola, Simo, Mkesh), cards, bank transfers, references, USSD, QR and BNPL behind one HTTP API.\n\nEvery account has a **test** and a **live** mode, selected by the API key you authenticate with. Connector credentials are bound per (account, mode, method), so sandbox traffic never touches live credentials. Test mode is deterministic: the same charge request with the same inputs produces the same outcome, so you can write end-to-end tests without depending on provider sandboxes.\n\nThe gateway accepts `POST /charges`, returns either an immediate result or a `next_action` block describing what the customer must do (redirect, OTP, QR, reference, etc.), and posts a signed webhook to `callback_url` when the payment terminalises (`payment.succeeded` or `payment.failed`).\n\n**Core conventions:**\n- Money is decimal: `\"amount\": \"19.99\"`, never integer minor units.\n- IDs are bare ULIDs/UUIDs; no `req_` / `pi_` / `cus_` typed prefixes.\n- No top-level `\"object\"` discriminator on responses.\n- Errors use [RFC 7807 Problem Details](https://www.rfc-editor.org/rfc/rfc7807) with a `code` extension.\n- Idempotent POSTs accept the `Idempotency-Key` header; replays of the same key return the original response.\n\n**Test inputs** are catalogued at [`/docs/testing`](/docs/testing).\n\n**Data residency.** Each tenant is served in one region (`mz`, `regional-sa`). A request that reaches another region is answered `421 Misdirected Request` with error `wrong_region`, the `region`, its `base_url`, and the headers `KulPay-Region` and `KulPay-Region-Url`. Resend it to that base URL.",
    "contact": {
      "name": "KulPay",
      "url": "https://kulpay.pavulla.com"
    },
    "license": {
      "name": "Proprietary \u2014 \u00a9 Pavulla Tech"
    }
  },
  "servers": [
    {
      "url": "/",
      "description": "This gateway"
    }
  ],
  "tags": [
    {
      "name": "Partners",
      "description": "PSPs and billers (LSPs) as KulPay presents them on channels: their logos."
    },
    {
      "name": "Bill payments",
      "description": "Billers, products and supply connectors (epics 14\u201319): prepaid electricity, top-ups, airtime and data, postpaid bills and vouchers."
    },
    {
      "name": "Charges",
      "description": "Create and manage payments."
    },
    {
      "name": "Refunds",
      "description": "Refund a settled payment."
    },
    {
      "name": "Payouts",
      "description": "Send funds to a recipient."
    },
    {
      "name": "Disputes",
      "description": "Chargebacks and claims against payments: held, answered with evidence, won or lost (F2-5)."
    },
    {
      "name": "Payments",
      "description": "Read-only payment snapshots."
    },
    {
      "name": "Manual evidence",
      "description": "Customer-side submission of deposit slips and reconciliation fields for `method=manual` payments."
    },
    {
      "name": "Admin",
      "description": "Operator-only endpoints. Live on the management surface (typically `:9091`); subject to bearer auth, mTLS, and IP allowlist when configured."
    },
    {
      "name": "Audit",
      "description": "Tamper-evident audit trail: the HTTP request log, per-tenant event hash chains, signed daily anchors in write-once storage, hourly verification, signed export bundles and unified timelines."
    },
    {
      "name": "Reconciliation",
      "description": "Match what a provider settled against what the gateway collected. Operator-only; lives on the management surface."
    },
    {
      "name": "Ledger",
      "description": "The double-entry ledger: a chart of accounts per tenant, mode and currency, and append-only balanced journals (PSP-10.1)."
    },
    {
      "name": "Events",
      "description": "Every state change as an immutable, ordered event. The same envelope is delivered by webhook and served here."
    },
    {
      "name": "Event pipeline",
      "description": "Operator view of the outbox relay, consumers, dead-letter queue and projection replays."
    },
    {
      "name": "PSP onboarding",
      "description": "A PSP applies, is reviewed, set up, certified and taken live through a recorded, approvable lifecycle."
    },
    {
      "name": "Reference",
      "description": "Reference data shared by every integration."
    },
    {
      "name": "Compliance",
      "description": "Suspicious-activity cases and communications to GIFiM (F3-8), compliance:* only."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/charges": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "Create a charge",
        "description": "Initiate a payment. Returns either a final outcome (`succeeded` / `failed`) or `requires_action` with a `next_action` block describing what the customer must do (redirect, OTP, QR, reference). Carries an `Idempotency-Key`-friendly contract: replays of the same `payment_id` return the original response. A card is sent as the acquirer's token only; a card number is refused with 400 `gateway.raw_card_data_not_accepted`, and the payment records `transaction.card.tokenized`.",
        "operationId": "createCharge",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChargeRequest"
              },
              "examples": {
                "mobileMoney": {
                  "summary": "M-Pesa charge",
                  "value": {
                    "payment_id": "01J9X8K2Y3M4N5P6Q7R8S9T0V1",
                    "money": {
                      "amount": "100.00",
                      "currency": "MZN"
                    },
                    "source": {
                      "type": "mobile_wallet",
                      "mobile_wallet": {
                        "network": "mpesa",
                        "phone_number": "+258840000001"
                      }
                    },
                    "customer": {
                      "email": "demo@example.test",
                      "name": "Demo Customer"
                    },
                    "country": "MZ",
                    "callback_url": "https://account.example.test/webhooks/kulpay",
                    "return_url": "https://account.example.test/return"
                  }
                },
                "card": {
                  "summary": "Card charge with 3DS",
                  "value": {
                    "payment_id": "01J9X8K2Y3M4N5P6Q7R8S9T0V2",
                    "money": {
                      "amount": "1500.00",
                      "currency": "MZN"
                    },
                    "source": {
                      "type": "card"
                    },
                    "customer": {
                      "email": "demo@example.test"
                    },
                    "country": "MZ",
                    "return_url": "https://account.example.test/return"
                  }
                },
                "openAmount": {
                  "summary": "Open-amount tip jar",
                  "value": {
                    "payment_id": "01J9X8K2Y3M4N5P6Q7R8S9T0V3",
                    "money": {
                      "amount": "0",
                      "currency": "MZN"
                    },
                    "source": {
                      "type": "card"
                    },
                    "customer": {
                      "name": "Anonymous"
                    },
                    "country": "MZ",
                    "open_amount": true,
                    "min_amount": "10.00",
                    "max_amount": "10000.00",
                    "suggested_amounts": [
                      "50.00",
                      "100.00",
                      "250.00"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Charge created. Inspect `status` and `next_action` to decide whether further action is required.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeResult"
                },
                "example": {
                  "payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "checkout_url": "https://merchant.example.co.mz/kulpay/return",
                  "next_action": {
                    "type": "redirect_to_url",
                    "redirect_url": "https://merchant.example.co.mz/kulpay/return",
                    "return_url": "https://merchant.example.co.mz/kulpay/return",
                    "otp_hint": "string",
                    "otp_length": 1,
                    "masked_dest": "string",
                    "qr_code_url": "https://merchant.example.co.mz/kulpay/return",
                    "qr_code_data": "string",
                    "entity": "string",
                    "reference": "2003014567",
                    "expires_at": "2026-10-10T09:30:00Z",
                    "poll_interval_secs": 1,
                    "authorization_code": "EDM",
                    "auth_expires_at": "2026-10-10T09:30:00Z",
                    "bnpl_provider": "mpesa"
                  },
                  "authorized_amount": "1250.00",
                  "captured_amount": "1250.00",
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "processed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "422": {
            "description": "Refused before any provider call: outside every eligible PSP's capability matrix (`not_supported`) or limits (`limit_exceeded`), naming the failing `rule` (PSP-5.3); or declined by the risk stage (`risk_declined`, PSP-6.3; the rule is not disclosed). No payment is created; `transaction.rejected` is recorded. `pilot_limit` while the account is a PSP in pilot: the live transaction breaks the pilot setting named in `rule` (`per_transaction`, `daily`, `monthly`, `currency`, `settings_missing`, or `merchant_not_allowed` for a `metadata.merchant_id` missing or not on the allow-list).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 422,
                  "code": "invalid_request",
                  "detail": "Refused before any provider call: outside every eligible PSP's capability matrix (not_supported) or limits (limit_exceeded), naming the failing rule (PSP-5.3); or declined by the risk stage (risk_d\u2026",
                  "error": {
                    "code": "invalid_request",
                    "message": "Refused before any provider call: outside every eligible PSP's capability matrix (not_supported) or limits (limit_exceeded), naming the failing rule (PSP-5.3); or declined by the risk stage (risk_d\u2026"
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "description": "`no_route`: a PSP could take it but none is available now (suspended, circuit open, no binding). Retry after `Retry-After` seconds.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "service_unavailable",
                  "detail": "no_route: a PSP could take it but none is available now (suspended, circuit open, no binding). Retry after Retry-After seconds.",
                  "error": {
                    "code": "service_unavailable",
                    "message": "no_route: a PSP could take it but none is available now (suspended, circuit open, no binding). Retry after Retry-After seconds."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/charges/{id}/capture": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "Capture an authorised charge",
        "description": "Capture funds previously held by an `authorize_capture` flow. Optionally captures a smaller amount than was authorised (partial capture).",
        "operationId": "captureCharge",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChargeID"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "$ref": "#/components/schemas/Money"
                  }
                }
              },
              "example": {
                "amount": {
                  "amount": "100.00",
                  "currency": "MZN"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Capture successful.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeResult"
                },
                "example": {
                  "payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "checkout_url": "https://merchant.example.co.mz/kulpay/return",
                  "next_action": {
                    "type": "redirect_to_url",
                    "redirect_url": "https://merchant.example.co.mz/kulpay/return",
                    "return_url": "https://merchant.example.co.mz/kulpay/return",
                    "otp_hint": "string",
                    "otp_length": 1,
                    "masked_dest": "string",
                    "qr_code_url": "https://merchant.example.co.mz/kulpay/return",
                    "qr_code_data": "string",
                    "entity": "string",
                    "reference": "2003014567",
                    "expires_at": "2026-10-10T09:30:00Z",
                    "poll_interval_secs": 1,
                    "authorization_code": "EDM",
                    "auth_expires_at": "2026-10-10T09:30:00Z",
                    "bnpl_provider": "mpesa"
                  },
                  "authorized_amount": "1250.00",
                  "captured_amount": "1250.00",
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "processed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/InvalidState"
          }
        }
      }
    },
    "/charges/{id}/void": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "Void an uncaptured charge",
        "description": "Release the held funds without capturing. Only valid for charges still in `authorized` state.",
        "operationId": "voidCharge",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChargeID"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Charge voided.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeResult"
                },
                "example": {
                  "payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "checkout_url": "https://merchant.example.co.mz/kulpay/return",
                  "next_action": {
                    "type": "redirect_to_url",
                    "redirect_url": "https://merchant.example.co.mz/kulpay/return",
                    "return_url": "https://merchant.example.co.mz/kulpay/return",
                    "otp_hint": "string",
                    "otp_length": 1,
                    "masked_dest": "string",
                    "qr_code_url": "https://merchant.example.co.mz/kulpay/return",
                    "qr_code_data": "string",
                    "entity": "string",
                    "reference": "2003014567",
                    "expires_at": "2026-10-10T09:30:00Z",
                    "poll_interval_secs": 1,
                    "authorization_code": "EDM",
                    "auth_expires_at": "2026-10-10T09:30:00Z",
                    "bnpl_provider": "mpesa"
                  },
                  "authorized_amount": "1250.00",
                  "captured_amount": "1250.00",
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "processed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/InvalidState"
          }
        }
      }
    },
    "/charges/{id}/complete-action": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "Complete a customer-action step",
        "description": "Tells the gateway the customer has finished an interactive step (redirect, OTP entry, QR scan). Use after the `next_action` URL returns the customer to your `return_url`.",
        "operationId": "completeChargeAction",
        "parameters": [
          {
            "$ref": "#/components/parameters/ChargeID"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "otp": {
                    "type": "string",
                    "description": "OTP value when completing display_otp_form."
                  }
                }
              },
              "example": {
                "otp": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Action completed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeResult"
                },
                "example": {
                  "payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "checkout_url": "https://merchant.example.co.mz/kulpay/return",
                  "next_action": {
                    "type": "redirect_to_url",
                    "redirect_url": "https://merchant.example.co.mz/kulpay/return",
                    "return_url": "https://merchant.example.co.mz/kulpay/return",
                    "otp_hint": "string",
                    "otp_length": 1,
                    "masked_dest": "string",
                    "qr_code_url": "https://merchant.example.co.mz/kulpay/return",
                    "qr_code_data": "string",
                    "entity": "string",
                    "reference": "2003014567",
                    "expires_at": "2026-10-10T09:30:00Z",
                    "poll_interval_secs": 1,
                    "authorization_code": "EDM",
                    "auth_expires_at": "2026-10-10T09:30:00Z",
                    "bnpl_provider": "mpesa"
                  },
                  "authorized_amount": "1250.00",
                  "captured_amount": "1250.00",
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "processed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/InvalidState"
          }
        }
      }
    },
    "/payments/{provider_payment_id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "summary": "Read a payment snapshot",
        "description": "Returns the current state of a payment. Use this to poll when the original charge returned `next_action.type=poll`, or to reconcile after a missed webhook.",
        "operationId": "getPaymentSnapshot",
        "parameters": [
          {
            "name": "provider_payment_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Gateway-side payment identifier (the `provider_payment_id` returned in the original `ChargeResult`)."
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentSnapshot"
                },
                "example": {
                  "payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "checkout_url": "https://merchant.example.co.mz/kulpay/return",
                  "next_action": {
                    "type": "redirect_to_url",
                    "redirect_url": "https://merchant.example.co.mz/kulpay/return",
                    "return_url": "https://merchant.example.co.mz/kulpay/return",
                    "otp_hint": "string",
                    "otp_length": 1,
                    "masked_dest": "string",
                    "qr_code_url": "https://merchant.example.co.mz/kulpay/return",
                    "qr_code_data": "string",
                    "entity": "string",
                    "reference": "2003014567",
                    "expires_at": "2026-10-10T09:30:00Z",
                    "poll_interval_secs": 1,
                    "authorization_code": "EDM",
                    "auth_expires_at": "2026-10-10T09:30:00Z",
                    "bnpl_provider": "mpesa"
                  },
                  "authorized_amount": "1250.00",
                  "captured_amount": "1250.00",
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "last_updated_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/refunds": {
      "post": {
        "tags": [
          "Refunds"
        ],
        "summary": "Create a refund",
        "description": "Refund all or part of a settled payment. Omit `amount` for a full refund.",
        "operationId": "createRefund",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundRequest"
              },
              "examples": {
                "fullRefund": {
                  "summary": "Full refund",
                  "value": {
                    "refund_id": "01J9X8K2Y3M4N5P6Q7R8S9T0V4",
                    "provider_payment_id": "pay_01J9X8K2Y3M4N5P6Q7R8S9T0V1",
                    "reason": "requested_by_customer"
                  }
                },
                "partialRefund": {
                  "summary": "Partial refund",
                  "value": {
                    "refund_id": "01J9X8K2Y3M4N5P6Q7R8S9T0V5",
                    "provider_payment_id": "pay_01J9X8K2Y3M4N5P6Q7R8S9T0V1",
                    "amount": {
                      "amount": "20.00",
                      "currency": "MZN"
                    },
                    "reason": "duplicate"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refund result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RefundResult"
                },
                "example": {
                  "refund_id": "rf-trace-1",
                  "provider_refund_id": "3KWPrnqkKWDuWJClOCrLq4csLVz",
                  "status": "succeeded",
                  "money": {
                    "amount": "2.00",
                    "currency": "MZN"
                  },
                  "processed_at": "2026-10-10T22:15:51Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/InvalidState"
          }
        }
      }
    },
    "/payouts": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "summary": "Create a payout",
        "description": "Send funds to a recipient (mobile-wallet, bank transfer, etc.). The gateway returns an immediate `pending` and posts a webhook when the payout settles.",
        "operationId": "createPayout",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoutRequest"
              },
              "example": {
                "payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "money": {
                  "amount": "100.00",
                  "currency": "MZN"
                },
                "destination": {
                  "type": "mobile_wallet",
                  "mobile_wallet": {
                    "network": "mpesa",
                    "phone_number": "+258840000001"
                  }
                },
                "recipient": {
                  "organization_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "user_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "name": "Electricidade de Mo\u00e7ambique",
                  "tax_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
                },
                "description": "Pagamento de energia, outubro",
                "callback_url": "https://merchant.example.co.mz/kulpay/return",
                "metadata": {
                  "key": "1250.00"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payout accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayoutResult"
                },
                "example": {
                  "payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "processed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/payouts/{provider_payout_id}": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "summary": "Read a payout snapshot",
        "operationId": "getPayoutSnapshot",
        "parameters": [
          {
            "name": "provider_payout_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayoutSnapshot"
                },
                "example": {
                  "payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "pending",
                  "money": {
                    "amount": "100.00",
                    "currency": "MZN"
                  },
                  "destination": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "last_updated_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/checkout/{id}/manual": {
      "post": {
        "tags": [
          "Manual evidence"
        ],
        "summary": "Submit manual-payment evidence",
        "description": "Customer-side submission for `method=manual` payments. Accepts `multipart/form-data`: any non-empty text field (depositor, bank, branch, reference, note, \u2026) merges into the payment's `manual_fields` map; an optional `file` part becomes a `manualAttachment` tagged `uploaded_by: customer`. Redirects (303) back to `/checkout/{id}/processing` with `?manual_submitted=1` on success or `?manual_error=\u2026` on file validation failure. Field merge is independent of file validation \u2014 a too-large upload doesn't lose typed fields.",
        "operationId": "submitManualEvidence",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "depositor": {
                    "type": "string"
                  },
                  "bank": {
                    "type": "string"
                  },
                  "branch": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  },
                  "kind": {
                    "type": "string",
                    "default": "deposit_slip"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "_csrf": {
                    "type": "string",
                    "description": "Required when `csrf_required: true` is set."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "303": {
            "description": "Redirect back to the processing page with a flash query param."
          },
          "400": {
            "$ref": "#/components/responses/ProblemDetails"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/status": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Posture snapshot",
        "description": "At-a-glance live state of every security knob: keys configured, mTLS / CSRF / audit toggles, webhook secret rotation status, idempotency cache fill, audit chain head + length, lifetime counters. Cheap; safe under `admin:read`.",
        "operationId": "adminStatus",
        "responses": {
          "200": {
            "description": "Posture snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "gateway_name": {
                      "type": "string"
                    },
                    "uptime_ms": {
                      "type": "integer"
                    },
                    "keys": {
                      "type": "object",
                      "properties": {
                        "configured": {
                          "type": "integer"
                        },
                        "test_keys": {
                          "type": "integer"
                        },
                        "live_keys": {
                          "type": "integer"
                        },
                        "legacy_tokens": {
                          "type": "integer"
                        }
                      }
                    },
                    "auth": {
                      "type": "object",
                      "properties": {
                        "open_dev_mode": {
                          "type": "boolean"
                        },
                        "csrf_required": {
                          "type": "boolean"
                        }
                      }
                    },
                    "webhook": {
                      "type": "object",
                      "properties": {
                        "secrets": {
                          "type": "integer"
                        },
                        "active_secrets": {
                          "type": "integer"
                        },
                        "legacy_single_secret": {
                          "type": "boolean"
                        },
                        "replay_tolerance": {
                          "type": "integer"
                        },
                        "outbound_mtls": {
                          "type": "boolean"
                        }
                      }
                    },
                    "mgmt": {
                      "type": "object",
                      "properties": {
                        "mtls": {
                          "type": "boolean"
                        },
                        "ip_allowlist_entries": {
                          "type": "integer"
                        }
                      }
                    },
                    "idempotency": {
                      "type": "object",
                      "properties": {
                        "capacity": {
                          "type": "integer"
                        },
                        "held": {
                          "type": "integer"
                        }
                      }
                    },
                    "audit": {
                      "type": "object",
                      "properties": {
                        "length": {
                          "type": "integer"
                        },
                        "capacity": {
                          "type": "integer"
                        },
                        "chain_head": {
                          "type": "string"
                        }
                      }
                    },
                    "counters": {
                      "type": "object",
                      "properties": {
                        "payments_total": {
                          "type": "integer"
                        },
                        "succeeded_total": {
                          "type": "integer"
                        },
                        "failed_total": {
                          "type": "integer"
                        },
                        "refunded_total": {
                          "type": "integer"
                        },
                        "expired_total": {
                          "type": "integer"
                        },
                        "webhooks_fired_total": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "gateway_name": "KulPay Local",
                  "uptime_ms": 557260,
                  "keys": {
                    "configured": 3,
                    "test_keys": 3,
                    "live_keys": 0,
                    "legacy_tokens": 0
                  },
                  "auth": {
                    "open_dev_mode": false,
                    "csrf_required": false
                  },
                  "webhook": {
                    "secrets": 0,
                    "active_secrets": 0,
                    "legacy_single_secret": true,
                    "replay_tolerance": 0,
                    "outbound_mtls": false
                  },
                  "mgmt": {
                    "mtls": false,
                    "ip_allowlist_entries": 0
                  },
                  "idempotency": {
                    "capacity": 0,
                    "held": 70
                  },
                  "audit": {
                    "length": 270,
                    "capacity": 10000,
                    "chain_head": "95f642a992b04a5b371cd8799e7b4bd9cd7a5d59e28ccea814263ce88211af6f"
                  },
                  "counters": {
                    "payments_total": 0,
                    "succeeded_total": 0,
                    "failed_total": 0,
                    "refunded_total": 0,
                    "expired_total": 1,
                    "webhooks_fired_total": 0
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/statements": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Import a provider settlement file",
        "description": "multipart/form-data: a `file` part plus `provider` (one of the registered parsers: `mpesa`, `emola`, `stripe`, `generic`).\n\nSupply `declared_total` whenever the provider's own portal reports one. It is the only reliable check that the file parsed correctly \u2014 a parser reading the wrong column produces rows that look entirely plausible one by one, and only the sum betrays it. When the parsed total disagrees the import is **refused**, because a mis-parsed statement yields false reconciliation results.\n\nParsing is all-or-nothing: an unreadable row fails the whole import rather than being skipped, since a silently dropped row is money vanishing from the reconciliation with nobody told.",
        "operationId": "importStatement",
        "responses": {
          "201": {
            "description": "Statement imported and parsed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "provider": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "gross_total": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "imported_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                  "provider": "mpesa",
                  "mode": "live",
                  "filename": "mpesa-settlement-2026-10-09.csv",
                  "status": "parsed",
                  "line_count": 142,
                  "gross_total": "187450.75",
                  "currency": "MZN",
                  "imported_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                  "created_at": "2026-10-10T07:42:18Z"
                }
              }
            }
          },
          "400": {
            "description": "Unknown provider, unparseable file, or declared-total mismatch. Nothing was written.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Import failed",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "import statement: parsed total 187450.75 does not match the declared total 187540.75; the file has not been imported because a mis-parsed statement produces false reconciliation results",
                  "error": {
                    "code": "invalid_request",
                    "message": "import statement: parsed total 187450.75 does not match the declared total 187540.75; the file has not been imported because a mis-parsed statement produces false reconciliation results"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "List imported statements",
        "operationId": "listStatements",
        "responses": {
          "200": {
            "description": "Statements, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {}
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [],
                  "total": 0
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/statements/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Statement summary with line counts and run history",
        "operationId": "getStatement",
        "responses": {
          "200": {
            "description": "Statement, per-status line counts, and every matching run over it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statement": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "provider": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "gross_total": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "imported_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "counts": {
                      "type": "object",
                      "properties": {
                        "matched": {
                          "type": "integer"
                        },
                        "exception": {
                          "type": "integer"
                        },
                        "resolved": {
                          "type": "integer"
                        }
                      }
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "statement_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "started_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "finished_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "total_lines": {
                            "type": "integer"
                          },
                          "matched_exact": {
                            "type": "integer"
                          },
                          "matched_inferred": {
                            "type": "integer"
                          },
                          "exceptions": {
                            "type": "integer"
                          },
                          "missing_in_provider": {
                            "type": "integer"
                          },
                          "started_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "deleted_at": {}
                        }
                      }
                    }
                  }
                },
                "example": {
                  "statement": {
                    "id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                    "provider": "mpesa",
                    "mode": "live",
                    "filename": "mpesa-settlement-2026-10-09.csv",
                    "status": "matched",
                    "line_count": 142,
                    "gross_total": "187450.75",
                    "currency": "MZN",
                    "imported_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "created_at": "2026-10-10T07:42:18Z"
                  },
                  "counts": {
                    "matched": 139,
                    "exception": 2,
                    "resolved": 1
                  },
                  "runs": [
                    {
                      "id": "3KXTUaDcjrx7ccHt2ad65hzKJr1",
                      "statement_id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "started_at": "2026-10-10T08:15:40Z",
                      "finished_at": "2026-10-10T08:15:42Z",
                      "total_lines": 142,
                      "matched_exact": 136,
                      "matched_inferred": 3,
                      "exceptions": 2,
                      "missing_in_provider": 1,
                      "started_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                      "created_at": "2026-10-10T08:15:40Z",
                      "updated_at": "2026-10-10T08:15:42Z",
                      "deleted_at": null
                    },
                    {
                      "id": "3KdcX9U9BNL1F1sbOSMOO5lBddu",
                      "statement_id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "started_at": "2026-10-10T07:45:01Z",
                      "finished_at": "2026-10-10T07:45:03Z",
                      "total_lines": 142,
                      "matched_exact": 136,
                      "matched_inferred": 2,
                      "exceptions": 4,
                      "missing_in_provider": 1,
                      "started_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                      "created_at": "2026-10-10T07:45:01Z",
                      "updated_at": "2026-10-10T07:45:03Z",
                      "deleted_at": null
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/statements/{id}/run": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Run a matching pass",
        "description": "Matches every line against the account's payments in tiers: exact provider reference first, then an inferred tier on amount + method + time window that requires exactly one candidate.\n\nThe engine never guesses. Where several payments fit equally well it raises an `ambiguous_match` exception rather than picking one \u2014 a wrong automatic match silently closes a discrepancy that was real, whereas an unmatched line gets looked at.\n\nRuns are deterministic and re-runnable: the same statement matched twice produces the same result, so re-running after fixing data shows only what the fix changed. Operator resolutions are preserved across re-runs.\n\nOptional body: `amount_tolerance` (absorb fee/rounding differences) and `infer_matches` (set `false` for reference-only matching).",
        "operationId": "runReconciliation",
        "responses": {
          "200": {
            "description": "Run summary: `matched_exact`, `matched_inferred`, `exceptions`, `missing_in_provider`, `exceptions_by_reason`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run_id": {
                      "type": "string"
                    },
                    "total_lines": {
                      "type": "integer"
                    },
                    "matched_exact": {
                      "type": "integer"
                    },
                    "matched_inferred": {
                      "type": "integer"
                    },
                    "exceptions": {
                      "type": "integer"
                    },
                    "missing_in_provider": {
                      "type": "integer"
                    },
                    "exceptions_by_reason": {
                      "type": "object",
                      "properties": {
                        "missing_in_gateway": {
                          "type": "integer"
                        },
                        "amount_mismatch": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "run_id": "3KXTUaDcjrx7ccHt2ad65hzKJr1",
                  "total_lines": 142,
                  "matched_exact": 136,
                  "matched_inferred": 3,
                  "exceptions": 3,
                  "missing_in_provider": 1,
                  "exceptions_by_reason": {
                    "missing_in_gateway": 1,
                    "amount_mismatch": 2
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/statements/{id}/lines": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "List statement lines",
        "description": "`?match_status=exception` is the exceptions queue.\n\nException reasons: `missing_in_provider` (the gateway says succeeded but the provider never settled \u2014 if goods shipped against it, that is a loss), `missing_in_gateway` (money arrived that was never credited to anyone), `amount_mismatch`, `currency_mismatch`, `duplicate_provider_line`, `status_mismatch`, `ambiguous_match`, `late_settlement`. The first two are the ones that cost real money.",
        "operationId": "listStatementLines",
        "responses": {
          "200": {
            "description": "Statement lines.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "statement_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "provider_ref": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "counterparty": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "fee": {
                            "type": "string"
                          },
                          "provider_status": {
                            "type": "string"
                          },
                          "occurred_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "raw": {
                            "type": "string"
                          },
                          "line_no": {
                            "type": "integer"
                          },
                          "match_status": {
                            "type": "string"
                          },
                          "match_confidence": {
                            "type": "string"
                          },
                          "matched_payment_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "deleted_at": {}
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KS4kWwcWwYGrimwzV2vQkmxyYX",
                      "statement_id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "provider_ref": "QJK4H7T2LP",
                      "method": "mpesa",
                      "counterparty": "*********567",
                      "amount": "1250.75",
                      "currency": "MZN",
                      "fee": "12.5",
                      "provider_status": "Completed",
                      "occurred_at": "2026-10-09T09:14:02Z",
                      "raw": "{\"Amount\":\"1250.75\",\"Fee\":\"12.50\",\"MSISDN\":\"*********567\",\"Receipt No.\":\"QJK4H7T2LP\",\"Status\":\"Completed\"}",
                      "line_no": 1,
                      "match_status": "matched",
                      "match_confidence": "exact",
                      "matched_payment_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                      "created_at": "2026-10-10T07:42:18Z",
                      "updated_at": "2026-10-10T07:45:03Z",
                      "deleted_at": null
                    },
                    {
                      "id": "3K8f1e6IJqZhU9nXve1BrPIsPnf",
                      "statement_id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "provider_ref": "QJK9M2X8RB",
                      "method": "mpesa",
                      "counterparty": "*********318",
                      "amount": "480.25",
                      "currency": "MZN",
                      "fee": "0",
                      "provider_status": "Completed",
                      "occurred_at": "2026-10-09T15:31:47Z",
                      "raw": "{\"Amount\":\"480.25\",\"Fee\":\"0.00\",\"MSISDN\":\"*********318\",\"Receipt No.\":\"QJK9M2X8RB\",\"Status\":\"Completed\"}",
                      "line_no": 2,
                      "match_status": "exception",
                      "exception_reason": "missing_in_gateway",
                      "exception_detail": "no payment recorded for this settlement line",
                      "created_at": "2026-10-10T07:42:18Z",
                      "updated_at": "2026-10-10T07:45:03Z",
                      "deleted_at": null
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/statements/{id}/close": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Close a fully worked statement",
        "description": "Refused while any exception is still open: the point of the queue is that a human looked at every discrepancy.",
        "operationId": "closeStatement",
        "responses": {
          "200": {
            "description": "Statement closed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "provider": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "gross_total": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "imported_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                  "provider": "mpesa",
                  "mode": "live",
                  "filename": "mpesa-settlement-2026-10-09.csv",
                  "status": "closed",
                  "line_count": 142,
                  "gross_total": "187450.75",
                  "currency": "MZN",
                  "imported_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                  "created_at": "2026-10-10T07:42:18Z"
                }
              }
            }
          },
          "409": {
            "description": "Exceptions still open.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Cannot close",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "close statement: 2 exception(s) still open; resolve them first",
                  "error": {
                    "code": "invalid_state",
                    "message": "close statement: 2 exception(s) still open; resolve them first"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/lines/{lineID}/resolve": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Resolve an exception",
        "description": "Records an operator's disposition: `resolution`, an optional `payment_id` to link a payment the engine refused to guess at, and optional `notes`. The original exception reason is retained alongside the resolution \u2014 the audit question is both what the discrepancy was and who decided what to do about it.",
        "operationId": "resolveException",
        "responses": {
          "200": {
            "description": "Exception resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "statement_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "provider_ref": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "counterparty": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee": {
                      "type": "string"
                    },
                    "provider_status": {
                      "type": "string"
                    },
                    "occurred_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "raw": {
                      "type": "string"
                    },
                    "line_no": {
                      "type": "integer"
                    },
                    "match_status": {
                      "type": "string"
                    },
                    "exception_reason": {
                      "type": "string"
                    },
                    "exception_detail": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "deleted_at": {},
                    "match_confidence": {
                      "type": "string"
                    },
                    "matched_payment_id": {
                      "type": "string"
                    },
                    "resolution": {
                      "type": "string"
                    },
                    "resolved_by": {
                      "type": "string"
                    },
                    "resolved_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "resolve_notes": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3K8f1e6IJqZhU9nXve1BrPIsPnf",
                  "statement_id": "3KVCyjDWE3gWP8EM6gikjJxRWcL",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "provider_ref": "QJK9M2X8RB",
                  "method": "mpesa",
                  "counterparty": "+258841234318",
                  "amount": "480.25",
                  "currency": "MZN",
                  "fee": "0",
                  "provider_status": "Completed",
                  "occurred_at": "2026-10-09T15:31:47Z",
                  "raw": "{\"Amount\":\"480.25\",\"Fee\":\"0.00\",\"MSISDN\":\"+258841234318\",\"Receipt No.\":\"QJK9M2X8RB\",\"Status\":\"Completed\"}",
                  "line_no": 2,
                  "match_status": "resolved",
                  "exception_reason": "missing_in_gateway",
                  "exception_detail": "no payment recorded for this settlement line",
                  "created_at": "2026-10-10T07:42:18Z",
                  "updated_at": "2026-10-10T09:02:11Z",
                  "deleted_at": null,
                  "match_confidence": "manual",
                  "matched_payment_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                  "resolution": "linked_manually",
                  "resolved_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                  "resolved_at": "2026-10-10T09:02:11Z",
                  "resolve_notes": "Customer paid with a second phone; confirmed with the merchant."
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payment-exceptions": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "List payment exceptions",
        "description": "Payments whose recorded state and a later report contradict each other about money (a success after a failure, a failure after a success). The status stays as recorded; an operator decides what really happened. `?status=open|resolved` (default: every status).",
        "operationId": "listPaymentExceptions",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "resolved"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Exceptions, newest first: `{\"data\": [{id, payment_id, account_id, mode, kind, from, to, source, detail, status, resolution, resolved_by, created_at, resolved_at}]}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "from": {
                            "type": "string"
                          },
                          "to": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPoJTiQpJE1O91etFp65N2ZtV",
                      "payment_id": "pay_mine",
                      "account_id": "3KWPoKRQLRAVHKXHdGFNAZRJODL",
                      "mode": "test",
                      "kind": "status_conflict",
                      "from": "failed",
                      "to": "succeeded",
                      "source": "",
                      "detail": "late success",
                      "status": "open",
                      "created_at": "2026-10-10T22:15:24.540916793Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/outcome-queries": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listOutcomeQueries",
        "summary": "Payments of unknown outcome",
        "description": "Payments whose PSP answer never came back after sending (timeout, dropped connection) or that stayed in flight past their window (PSP-6.6). Each stays processing while status queries run on a schedule (10 s, 30 s, 1 min, 2 min, then every 5 min), by the PSP reference if one came back, else by the reference KulPay sent, until a conclusive status or the window closes (30 min, or the capability matrix's status_window_seconds). Then a reversal is sent where the PSP supports it (the payment ends cancelled); else a missing_at_psp payment exception is raised. ?state=querying|resolved|reversed|exception. Events: transaction.outcome.unknown, transaction.status_query.sent, transaction.reversal.requested.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "querying",
                "resolved",
                "reversed",
                "exception"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resolutions, newest first: {payment_id, psp_id, reason, state, attempts, last_result, next_query_at, window_until, started_at, closed_at}.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "payment_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "next_query_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "window_until": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "started_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "payment_id": "pay_unknown_a",
                      "account_id": "3KWPoNjHQzYjZcNZ1U2Ah7pSXAT",
                      "mode": "test",
                      "reason": "submit_unknown",
                      "state": "querying",
                      "attempts": 0,
                      "next_query_at": "2026-10-10T22:15:25.063321293Z",
                      "window_until": "2026-10-10T23:15:25.063321293Z",
                      "started_at": "2026-10-10T22:15:25.063321293Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payment-exceptions/{id}/resolve": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "summary": "Resolve a payment exception",
        "description": "Closes an open exception with what was found (`resolution`, required). Records reconciliation.payment_exception.resolved. The payment status is not changed here: correct money through a refund or an adjustment.",
        "operationId": "resolvePaymentException",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "resolution"
                ],
                "properties": {
                  "resolution": {
                    "type": "string",
                    "maxLength": 1000
                  }
                }
              },
              "example": {
                "resolution": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The resolved exception.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "payment_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "resolution": {
                      "type": "string"
                    },
                    "resolved_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "resolved_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K7VlByQ2XZdGAqlDZqjNT3fw7M",
                  "payment_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "kind": "status_conflict",
                  "from": "failed",
                  "to": "succeeded",
                  "source": "callback:mpesa",
                  "detail": "success callback for a payment already failed",
                  "status": "resolved",
                  "resolution": "Provider confirmed the debit; refunded the customer 1250.75 MZN.",
                  "resolved_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                  "created_at": "2026-10-08T17:22:09Z",
                  "resolved_at": "2026-10-09T09:41:30Z"
                }
              }
            }
          },
          "400": {
            "description": "No resolution, or already resolved.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Resolve failed",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a resolution is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a resolution is required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such exception.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "No such payment exception.",
                  "error": {
                    "code": "not_found",
                    "message": "No such payment exception."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Read the audit feed",
        "description": "Newest-first feed of every state-changing request. Each entry carries `key_id`, `action`, `target`, `ip`, `body_hash`, `status`, `outcome`, `prev_hash`, `hash`. Cap with `?limit=N` (default 100, max 1000).",
        "operationId": "listAudit",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "key_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "actor": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "target": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "ip": {
                            "type": "string"
                          },
                          "user_agent": {
                            "type": "string"
                          },
                          "body_hash": {
                            "type": "string"
                          },
                          "status": {
                            "type": "integer"
                          },
                          "outcome": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "prev_hash": {
                            "type": "string"
                          },
                          "hash": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "next_cursor": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "entries": [
                    {
                      "id": "pay_d0bb914e4510893d3ef98308",
                      "at": "2026-10-11T10:12:44Z",
                      "key_id": "3KIhzpLdNupTuLgu8bwZOQspar2",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "actor": "key:3KIhzpLdNupTuLgu8bwZOQspar2",
                      "action": "payment.refund",
                      "target": "3KjLY7BV6lTzyJvGXr3mZ6e2cY8",
                      "method": "POST",
                      "path": "/v1/payments/3KjLY7BV6lTzyJvGXr3mZ6e2cY8/refund",
                      "ip": "196.28.240.17",
                      "user_agent": "kulpay-node/2.4.0",
                      "body_hash": "e454a9599172d285c27e688ef7618a5a2e06295d8e30336299cb46a54391d5a7",
                      "status": 200,
                      "outcome": "succeeded",
                      "request_id": "req_0ec9edbeb109266ce7ef1836",
                      "prev_hash": "a4708e0610200009884b5e2dcda4da8199bfda23637532b7816d01a9df12b7ef",
                      "hash": "57d24fde044989614455651b64b658e1b8592b1abd7e9154a747a82954685876"
                    },
                    {
                      "id": "pay_e988f4b30c3835d77caa505e",
                      "at": "2026-10-11T09:58:02Z",
                      "key_id": "3KIhzpLdNupTuLgu8bwZOQspar2",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "actor": "key:3KIhzpLdNupTuLgu8bwZOQspar2",
                      "action": "post.v1/webhook-endpoints",
                      "method": "POST",
                      "path": "/v1/webhook-endpoints",
                      "ip": "196.28.240.17",
                      "user_agent": "kulpay-node/2.4.0",
                      "body_hash": "01fda5546683ecf0ee16789b031e4f685554be92cd9c589d42d2ecc088bafaa0",
                      "status": 201,
                      "outcome": "succeeded",
                      "prev_hash": "67e81257b728d54b76d9d1ac4fd9e03bb966cb5a8acd8f23140503bd8cfb39fc",
                      "hash": "a4708e0610200009884b5e2dcda4da8199bfda23637532b7816d01a9df12b7ef"
                    }
                  ],
                  "count": 2,
                  "next_cursor": "pay_d0bb914e4510893d3ef98308"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/verify": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Verify the audit hash chain",
        "description": "Walks every audit entry chronologically and confirms that each entry's `hash` equals `sha256(canonical(entry))` AND each `prev_hash` equals the previous entry's `hash`. Returns 200 when the chain is intact, 409 with `broken_at` and `reason` otherwise.",
        "operationId": "verifyAuditChain",
        "responses": {
          "200": {
            "description": "Chain intact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chain_head": {
                      "type": "string"
                    },
                    "length": {
                      "type": "integer"
                    },
                    "verified": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "chain_head": "",
                  "length": 0,
                  "verified": true
                }
              }
            }
          },
          "409": {
            "description": "Tampering detected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "type": "boolean"
                    },
                    "broken_at": {
                      "type": "integer"
                    },
                    "reason": {
                      "type": "string"
                    },
                    "chain_head": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "verified": false,
                  "broken_at": 1842,
                  "reason": "hash mismatch at position 1842",
                  "chain_head": "57d24fde044989614455651b64b658e1b8592b1abd7e9154a747a82954685876"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/webhooks/verify": {
      "post": {
        "tags": [
          "Admin"
        ],
        "summary": "Verify a signed webhook body",
        "description": "Oracle endpoint for integrators writing signing logic on the receiver side. POST a body + `KulPay-Signature: t=<unix>,v1=<hex>` header; the gateway runs `verifyKulPay` against its configured webhook secrets and replay tolerance. Returns 200 `{verified: true}` on success or 401 with a descriptive error code (`missing_signature`, `signature_invalid`, `signature_timestamp_outside_tolerance`).",
        "operationId": "verifyInboundSignature",
        "parameters": [
          {
            "name": "KulPay-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1715000000,v1=abcdef\u2026"
            },
            "description": " The deprecated X-KulPay-Signature is still accepted."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "*/*": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signature verified against an active secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "type": "boolean"
                    },
                    "secrets_considered": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "verified": true,
                  "secrets_considered": 2
                }
              }
            }
          },
          "401": {
            "description": "Missing / invalid / stale signature.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "t": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "error": "signature_invalid",
                  "message": "signature_timestamp_outside_tolerance",
                  "t": 1791712800
                }
              }
            }
          },
          "503": {
            "description": "Gateway has no webhook secret configured.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "error": {
                    "code": "no_secret_configured",
                    "message": "gateway has no webhook secret configured; signature verification not possible"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments/{id}/audit": {
      "get": {
        "tags": [
          "Audit"
        ],
        "summary": "Read the audit feed for one payment",
        "description": "Same shape as `/v1/admin/audit` filtered to entries whose `target` matches the path parameter.",
        "operationId": "listPaymentAudit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit entries for this payment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "key_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "actor": {
                            "type": "string"
                          },
                          "action": {
                            "type": "string"
                          },
                          "target": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "ip": {
                            "type": "string"
                          },
                          "user_agent": {
                            "type": "string"
                          },
                          "body_hash": {
                            "type": "string"
                          },
                          "status": {
                            "type": "integer"
                          },
                          "outcome": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "prev_hash": {
                            "type": "string"
                          },
                          "hash": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "count": {
                      "type": "integer"
                    },
                    "next_cursor": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "entries": [
                    {
                      "id": "pay_d0bb914e4510893d3ef98308",
                      "at": "2026-10-11T10:12:44Z",
                      "key_id": "3KIhzpLdNupTuLgu8bwZOQspar2",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "actor": "key:3KIhzpLdNupTuLgu8bwZOQspar2",
                      "action": "payment.refund",
                      "target": "3KjLY7BV6lTzyJvGXr3mZ6e2cY8",
                      "method": "POST",
                      "path": "/v1/payments/3KjLY7BV6lTzyJvGXr3mZ6e2cY8/refund",
                      "ip": "196.28.240.17",
                      "user_agent": "kulpay-node/2.4.0",
                      "body_hash": "e454a9599172d285c27e688ef7618a5a2e06295d8e30336299cb46a54391d5a7",
                      "status": 200,
                      "outcome": "succeeded",
                      "request_id": "req_0ec9edbeb109266ce7ef1836",
                      "prev_hash": "a4708e0610200009884b5e2dcda4da8199bfda23637532b7816d01a9df12b7ef",
                      "hash": "57d24fde044989614455651b64b658e1b8592b1abd7e9154a747a82954685876"
                    },
                    {
                      "id": "pay_be87706e93eb60fa46fb7d8b",
                      "at": "2026-10-08T14:21:12Z",
                      "key_id": "3KIhzpLdNupTuLgu8bwZOQspar2",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "actor": "key:3KIhzpLdNupTuLgu8bwZOQspar2",
                      "action": "payment.capture",
                      "target": "3KjLY7BV6lTzyJvGXr3mZ6e2cY8",
                      "method": "POST",
                      "path": "/v1/payments/3KjLY7BV6lTzyJvGXr3mZ6e2cY8/capture",
                      "ip": "196.28.240.17",
                      "user_agent": "kulpay-node/2.4.0",
                      "body_hash": "fd8747f4bca05336bbe57747d664100e9e6123cec386f8249e9bd0db0343a15c",
                      "status": 200,
                      "outcome": "succeeded",
                      "prev_hash": "127eb02825bf27f39d3330cb11c7f12aa587e25bf0582310d44c10c8163fb90b",
                      "hash": "67e81257b728d54b76d9d1ac4fd9e03bb966cb5a8acd8f23140503bd8cfb39fc"
                    }
                  ],
                  "count": 2,
                  "next_cursor": "pay_d0bb914e4510893d3ef98308"
                }
              }
            }
          }
        }
      }
    },
    "/methods": {
      "get": {
        "tags": [
          "Methods"
        ],
        "summary": "List the payment rails this gateway offers",
        "description": "The machine-readable source of truth for which rails are available and how to invoke each one. **Call this first** \u2014 it is what the hosted checkout builds its picker from, so it is exactly what a customer can choose.\n\nEach entry carries `kind` (the `source.type` to send on POST /charges), `requires_phone`, the currency and country filters, amount bounds, and `connector` \u2014 the implementation behind the rail. A connector named `mock_*` does NOT move money; in live mode the checkout refuses to offer those, so a rail present in test mode may legitimately be absent in live.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The catalogue, plus the gateway's current mode.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "methods": [
                    {
                      "method": "bank_redirect",
                      "kind": "bank_redirect",
                      "label": "Bank Redirect",
                      "subtitle": "Open banking / PSD2",
                      "min_amount": 0.01,
                      "max_amount": 50000000,
                      "connector": "mock_redirect"
                    },
                    {
                      "method": "bank_transfer",
                      "kind": "bank_transfer",
                      "label": "Bank Transfer",
                      "subtitle": "Direct transfer",
                      "min_amount": 100,
                      "max_amount": 50000000,
                      "connector": "mock_bank_transfer"
                    }
                  ],
                  "mode": "test"
                }
              }
            }
          }
        }
      }
    },
    "/transactions": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "List balance-transaction ledger entries",
        "description": "The double-entry ledger behind the account balance: one entry per money movement, newest first. Use it to reconcile your own books against the gateway rather than re-deriving totals from payments.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": [
                    {
                      "id": "3KVxleinLQ2aTdUMV3j8AO1Vgh6",
                      "type": "bill_payment",
                      "amount": "-600",
                      "currency": "MZN",
                      "payment_id": "3KVxlhCdCXyESvttlveMcwUUJgT",
                      "created_at": "2026-10-10T18:24:48.555359Z"
                    },
                    {
                      "id": "3KVxlevLCb7yGjg4TOzWNv34oYD",
                      "type": "charge",
                      "amount": "600",
                      "currency": "MZN",
                      "payment_id": "3KVxlf1OV0yh7HRHj7bgjQlmS4D",
                      "created_at": "2026-10-10T18:24:48.518159Z"
                    }
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to one entry kind."
          }
        ]
      }
    },
    "/files": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "List uploaded files",
        "description": "Files held for this account \u2014 proof-of-payment slips, statements for reconciliation.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The file list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": []
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Upload a file",
        "description": "multipart/form-data. Used for manual-payment evidence and reconciliation statement imports. Bytes are persisted, so an upload survives a restart.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "purpose": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored file's metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "purpose": {
                      "type": "string"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "content_type": {
                      "type": "string"
                    },
                    "size": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KFmKhlYELQZGQLw3WZICSjn9u0",
                  "mode": "test",
                  "purpose": "manual_payment_proof",
                  "filename": "comprovativo-transferencia.pdf",
                  "content_type": "application/pdf",
                  "size": 182344,
                  "created_at": "2026-10-11T09:14:37Z"
                }
              }
            }
          }
        }
      }
    },
    "/files/{id}": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Get a file's metadata",
        "description": "Metadata only; the bytes are at /files/{id}/content.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "File metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {}
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/files/{id}/content": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a file's bytes",
        "description": "Returns the stored bytes with their original content type.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {}
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/payments/{id}/files": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "List files attached to a payment",
        "description": "Proof-of-payment evidence attached to a manual/cash payment.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Attached files.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {}
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/admin/webhooks/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook delivery attempts",
        "description": "Every delivery the gateway has attempted, newest first \u2014 with attempt count, the response code your endpoint returned, and the error if it failed. This is how you debug a webhook your server never acknowledged.\n\nManagement surface: requires an admin key.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": [],
                  "limit": 50,
                  "offset": 0,
                  "total": 0
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "exhausted"
              ]
            }
          },
          {
            "name": "payment_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ]
      }
    },
    "/v1/admin/webhooks/deliveries/{id}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get one delivery, including the signed body",
        "description": "Returns the **frozen payload** \u2014 the exact bytes the signature was computed over. Re-serialising that JSON changes key order or spacing and produces a different HMAC, which sends people hunting a signature bug that is not there. Compare against the raw body your server received.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery, with `payload` as a verbatim string.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {}
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/me": {
      "get": {
        "summary": "What am I authenticated as?",
        "description": "Describes the calling key: its id, label, mode, the account it acts for, and its scopes. Requires NO scope \u2014 a key may always describe itself, and gating this would mean the key most likely to be misconfigured is the one that cannot find out why.\n\nCall this first when something returns 401 or 403. It distinguishes the three causes that otherwise look identical: a key that was never sent, a key in the wrong mode (charges succeed, the dashboard you are watching stays empty), and a key acting for a different account than you expect.\n\nNever returns the secret.",
        "operationId": "whoAmI",
        "tags": [
          "Diagnostics"
        ],
        "responses": {
          "200": {
            "description": "The calling identity. Also returned, with `authenticated: false`, when the gateway has no key registry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "authenticated": {
                      "type": "boolean",
                      "description": "False when the gateway has no key registry, so requests are unauthenticated and every scope check passes."
                    },
                    "key_id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "test",
                        "live"
                      ],
                      "description": "From the KEY, never the deployment: a test key on a live gateway reports test."
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "account_name": {
                      "type": "string"
                    },
                    "account_status": {
                      "type": "string",
                      "description": "`suspended` here explains charges being refused with 403."
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "rate_per_minute": {
                      "type": "integer",
                      "description": "This key's own limit, so a 429 can be told from the surface-wide one."
                    },
                    "note": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "authenticated",
                    "scopes"
                  ]
                },
                "example": {
                  "authenticated": true,
                  "key_id": "535163a20e6ce0cae10b379d29134a7f",
                  "label": "local merchant test key",
                  "mode": "test",
                  "account_id": "OcThktmhLjl2JAewdDBNXh2u0lT",
                  "account_name": "KulPay Local",
                  "account_status": "active",
                  "scopes": [
                    "payments:read",
                    "payments:write"
                  ],
                  "rate_per_minute": 6000,
                  "note": "Test mode: real provider calls where the provider has a sandbox, but no money moves. A test key can never read or write live data."
                }
              }
            }
          },
          "401": {
            "description": "No bearer token, or one the registry does not hold.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unauthorized",
                  "status": 401,
                  "code": "unauthorized",
                  "detail": "missing bearer token",
                  "error": {
                    "code": "unauthorized",
                    "message": "missing bearer token"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listEvents",
        "summary": "List events, or catch up after an event",
        "description": "Two modes.\n\n**Browse** (no `since`): newest first, filtered by `type`, `aggregate_id` and `created[gte|lte]`, paged with `starting_after`.\n\n**Catch up** (`since=<event id>`, or `since=` for the beginning): every later event, oldest first. Use it to recover anything a webhook endpoint missed. The newest 2 seconds are held back so no event is ever skipped by a transaction still committing.\n\nEvents of another account are never returned. Requires `events:read`.",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Event id to continue after (catch-up mode)."
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Event type; repeat or comma-separate for several."
          },
          {
            "name": "aggregate_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "created[gte]",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "RFC 3339 or Unix seconds."
          },
          {
            "name": "created[lte]",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "RFC 3339 or Unix seconds."
          },
          {
            "name": "starting_after",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "inbound, outbound or internal (PSP-7.1).",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound",
                "internal"
              ]
            }
          },
          {
            "name": "pattern",
            "in": "query",
            "description": "The interaction pattern (PSP-7.1).",
            "schema": {
              "type": "string",
              "enum": [
                "outbound_sync",
                "outbound_async",
                "outbound_notification",
                "inbound_request",
                "inbound_callback",
                "inbound_file",
                "outbound_file",
                "status_query"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of events.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventList"
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPm6bJc7T4d0U43IOgK0pT0vS",
                      "type": "transaction.stage.notify.skipped",
                      "schema_version": 1,
                      "aggregate_type": "payment",
                      "aggregate_id": "3KWPm4BeofWfvUSB1Cnv95jEq01",
                      "sequence": 10,
                      "occurred_at": "2026-10-10T22:15:06.718012Z",
                      "recorded_at": "2026-10-10T22:15:06.718012Z",
                      "created_at": "2026-10-10T22:15:06.718012Z",
                      "tenant_id": "3KWPm886QpMrxawEJqc4BES8NmT",
                      "mode": "test",
                      "direction": "outbound",
                      "pattern": "outbound_sync",
                      "actor": {
                        "type": "system",
                        "id": "system"
                      },
                      "data": {
                        "attempt": 1,
                        "detail": {
                          "endpoints": 0,
                          "event_id": "3KWPm3lMXjY9xox1gUUd8AoRJNc",
                          "event_type": "payment.succeeded"
                        },
                        "duration_ms": 0,
                        "ended_at": "2026-10-10T22:15:06.71700216Z",
                        "id": "3KWPm9Zsu3GgFBtCrrl83wZo7ML",
                        "outcome": "skipped",
                        "payment_id": "3KWPm4BeofWfvUSB1Cnv95jEq01",
                        "reason_code": "no_endpoints",
                        "stage": "notify",
                        "started_at": "2026-10-10T22:15:06.71700216Z",
                        "transaction_type": "SALE"
                      }
                    }
                  ],
                  "has_more": true,
                  "next_cursor": "3KWPm6bJc7T4d0U43IOgK0pT0vS"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameter (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid created[gte]",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "use RFC 3339 (2026-10-08T10:00:00Z) or Unix seconds",
                  "error": {
                    "code": "invalid_request",
                    "message": "use RFC 3339 (2026-10-08T10:00:00Z) or Unix seconds"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing `events:read` (`insufficient_scope`) or a key not bound to an account (`account_required`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: events:read",
                  "missing_scope": "events:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: events:read",
                    "missing_scope": "events:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown cursor (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unknown cursor",
                  "status": 404,
                  "code": "not_found",
                  "detail": "No event with that id exists for this account.",
                  "error": {
                    "code": "not_found",
                    "message": "No event with that id exists for this account."
                  }
                }
              }
            }
          },
          "503": {
            "description": "No event store in this deployment (`service_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "service_unavailable",
                  "detail": "No event store in this deployment (service_unavailable).",
                  "error": {
                    "code": "service_unavailable",
                    "message": "No event store in this deployment (service_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "getEvent",
        "summary": "Get one event",
        "description": "An event of another account or mode is reported as not found. Requires `events:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                },
                "example": {
                  "id": "3KWPcDFQR4Iy59TZIVI6Hf51z3k",
                  "type": "transaction.stage.enrich_validate.passed",
                  "schema_version": 1,
                  "aggregate_type": "payment",
                  "aggregate_id": "3KWPcChMXO2voAp3Vdqu6ZLEMQv",
                  "sequence": 3,
                  "occurred_at": "2026-10-10T22:13:47.674035Z",
                  "recorded_at": "2026-10-10T22:13:47.674035Z",
                  "created_at": "2026-10-10T22:13:47.674035Z",
                  "tenant_id": "OcThktmhLjl2JAewdDBNXh2u0lT",
                  "mode": "test",
                  "region": "mz",
                  "direction": "inbound",
                  "pattern": "inbound_request",
                  "actor": {
                    "type": "api_key",
                    "id": "535163a20e6ce0cae10b379d29134a7f",
                    "ip": "203.0.113.10",
                    "user_agent": "node"
                  },
                  "correlation_id": "b1768c81bbe474585ca1474a",
                  "idempotency_key": "ux-1791670427346",
                  "data": {
                    "attempt": 1,
                    "duration_ms": 3,
                    "ended_at": "2026-10-10T22:13:47.643753809Z",
                    "id": "3KWPc7hVNdQdhtS532Wttp6sORT",
                    "outcome": "passed",
                    "payment_id": "3KWPcChMXO2voAp3Vdqu6ZLEMQv",
                    "stage": "enrich_validate",
                    "started_at": "2026-10-10T22:13:47.640372197Z",
                    "transaction_type": "SALE"
                  },
                  "chain_seq": 320,
                  "prev_hash": "d88f24bf1fbd0b40ce5736661f2e46f09aee5faf699c836c1322b34cc46a324d",
                  "hash": "504e00e8a3ab657f3dbbbbdc52542302f9e54b7f03142cb24757999cff2a10bc"
                }
              }
            }
          },
          "404": {
            "description": "No such event (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "No such event",
                  "status": 404,
                  "code": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": ""
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/events/pipeline": {
      "get": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "getEventPipeline",
        "summary": "Outbox, consumers and dead-letter summary",
        "description": "Requires `admin:read`.",
        "responses": {
          "200": {
            "description": "Pipeline status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPipeline"
                },
                "example": {
                  "bus": "db",
                  "metrics": {
                    "published": 147,
                    "publish_failures": 0,
                    "processed": 147,
                    "duplicates": 0,
                    "retries": 0,
                    "dead_lettered": 0,
                    "unpublished": 0,
                    "oldest_unpublished_age": 0,
                    "lag_alert": false,
                    "stale_gaps": 0,
                    "publish_lag_p99": 596568000
                  },
                  "unpublished": 0,
                  "oldest_unpublished_age_seconds": 0,
                  "consumers": [
                    {
                      "consumer": "projection.event_stats",
                      "started_at": "2026-10-08T10:38:02.854371Z",
                      "cursor_at": "2026-10-10T22:22:46.913204Z",
                      "cursor_event_id": "3KWQht6ShKw2GvsxLg91zBsjsyX"
                    }
                  ],
                  "open_dead_letters": 0
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/events/dead-letters": {
      "get": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "listDeadLetters",
        "summary": "List dead-lettered events",
        "description": "Events a consumer gave up on after its maximum attempts, with the last error. Requires `admin:read`.",
        "parameters": [
          {
            "name": "consumer",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_replayed",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Dead letters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeadLetter"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPmXrzwUTODrmptisEQ6dOEIE",
                      "consumer": "test.flaky",
                      "event_id": "3KWPmSNckmxcSVVyjYjwiyknc9S",
                      "event_type": "transaction.stage.notify.skipped",
                      "tenant_id": "3KWPmY5oeJFG8Ct0XNDvwiGGOhG",
                      "attempts": 1,
                      "error": "downstream unavailable",
                      "created_at": "2026-10-10T22:15:09.502740134Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/events/dead-letters/{id}/replay": {
      "post": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "replayDeadLetter",
        "summary": "Replay a dead-lettered event",
        "description": "Runs the event through its consumer again, once, recorded under the caller. A second failure dead-letters it again. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Replayed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "replayed": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "replayed": true
                }
              }
            }
          },
          "404": {
            "description": "No such dead letter (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "No such dead letter (not_found).",
                  "error": {
                    "code": "not_found",
                    "message": "No such dead letter (not_found)."
                  }
                }
              }
            }
          },
          "409": {
            "description": "Already replayed (`invalid_state`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "Already replayed (invalid_state).",
                  "error": {
                    "code": "invalid_state",
                    "message": "Already replayed (invalid_state)."
                  }
                }
              }
            }
          },
          "502": {
            "description": "The consumer failed again (`replay_failed`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Gateway",
                  "status": 502,
                  "code": "internal_error",
                  "detail": "The consumer failed again (replay_failed).",
                  "error": {
                    "code": "internal_error",
                    "message": "The consumer failed again (replay_failed)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/events/replays": {
      "get": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "listReplays",
        "summary": "List projection replays",
        "description": "Requires `admin:read`.",
        "parameters": [
          {
            "name": "projection",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Replays, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProjectionReplay"
                      }
                    }
                  }
                },
                "example": {
                  "data": []
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "startReplay",
        "summary": "Rebuild a projection from the event store",
        "description": "Rebuilds whole closed UTC days into a shadow table and swaps it in only if its row count and checksum match an independent count of the event store. Never sends webhooks or calls providers. Requires `admin:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "to"
                ],
                "properties": {
                  "projection": {
                    "type": "string",
                    "default": "event_stats"
                  },
                  "from": {
                    "type": "string",
                    "format": "date",
                    "example": "2026-10-01"
                  },
                  "to": {
                    "type": "string",
                    "format": "date",
                    "description": "Inclusive; must be before today (UTC).",
                    "example": "2026-10-07"
                  }
                }
              },
              "example": {
                "projection": "event_stats",
                "from": "2026-10-01",
                "to": "2026-10-07"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The completed replay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProjectionReplay"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "projection": "event_stats",
                  "from": "2026-10-10T09:30:00Z",
                  "to": "2026-10-10T09:30:00Z",
                  "status": "running",
                  "events_read": 1,
                  "rows_written": 1,
                  "checksum": "string",
                  "error": "string",
                  "started_by": "string",
                  "started_at": "2026-10-10T09:30:00Z",
                  "completed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Bad range, today included, or unknown projection (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid replay",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "eventbus: a replay can only cover closed days (up to yesterday, UTC)",
                  "error": {
                    "code": "invalid_request",
                    "message": "eventbus: a replay can only cover closed days (up to yesterday, UTC)"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The rebuild did not match the event store; nothing was swapped (`invalid_state`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "The rebuild did not match the event store; nothing was swapped (invalid_state).",
                  "error": {
                    "code": "invalid_state",
                    "message": "The rebuild did not match the event store; nothing was swapped (invalid_state)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/secret-scanning/github": {
      "post": {
        "tags": [
          "Reference"
        ],
        "operationId": "githubSecretScanning",
        "summary": "GitHub secret scanning partner endpoint",
        "description": "GitHub posts KulPay keys it finds in public (pattern `kp_(live|test)_(sk|rk|pk)_[0-9A-Za-z]{49}`), signed with ECDSA P-256 (`Github-Public-Key-Identifier`, `Github-Public-Key-Signature` over the raw body, keys from api.github.com/meta/public_keys/secret_scanning) (PSP-8.6). No API key. A genuine live key is revoked at once, its account emailed and `credential.api_key.leaked` recorded; a test key is reported, revoked only with `secret_scanning.revoke_test_keys`.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": [
                {
                  "token": "tok_7Hq2\u2026",
                  "type": "standard",
                  "url": "https://merchant.example.co.mz/kulpay/return",
                  "source": "string"
                }
              ]
            }
          }
        },
        "responses": {
          "200": {
            "description": "`[{token_raw, token_type, label: true_positive|false_positive}]`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "token_raw": {
                        "type": "string"
                      },
                      "token_type": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": [
                  {
                    "token_raw": "kp_live_sk_ZKy7C9e7AjvOQLo0b3lRbXJ7i2NbMErBEocvYshqpC34H2L23",
                    "token_type": "kulpay_api_key",
                    "label": "true_positive"
                  }
                ]
              }
            }
          },
          "401": {
            "description": "`invalid_signature`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unauthorized",
                  "status": 401,
                  "code": "invalid_signature",
                  "detail": "the report's signature does not verify",
                  "error": {
                    "code": "invalid_signature",
                    "message": "the report's signature does not verify"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/callbacks/{psp_id}/{binding_id}": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "bindingCallback",
        "summary": "A PSP callback to its own binding",
        "description": "Where a PSP sends its callbacks for one connector binding (PSP-7.3); the URL is shown with the binding (`callback.url` on /v1/admin/connectors and /admin/connectors). No API key: the callback is verified with the binding's own configuration, which is the driver's signature check under the tenant's credentials (default), `hmac` (`X-KulPay-Signature: sha256=<hex HMAC-SHA256 of the body>`), `token` (`X-Callback-Token`, or `?token=`), `certificate` (a pinned client certificate, when KulPay terminates TLS), or `none` (test mode only), plus an optional IP allow-list. A verified callback is acknowledged at once and processed asynchronously with the tenant's connector, which settles only that tenant's payments. Events: callback.received, then callback.verified or callback.rejected.",
        "security": [],
        "parameters": [
          {
            "name": "psp_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The PSP's id, or the account's."
          },
          {
            "name": "binding_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "The provider's own payload, at most 1 MiB.",
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{\"received\": true}`: verified and queued (a retry of a queued callback answers the same).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "received": true
                }
              }
            }
          },
          "401": {
            "description": "`gateway.callback_rejected`: failed verification; the raw request is kept for investigation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Callback rejected",
                  "status": 401,
                  "code": "callback_rejected",
                  "detail": "The callback did not pass this binding's verification.",
                  "error": {
                    "code": "callback_rejected",
                    "message": "The callback did not pass this binding's verification."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such endpoint (unknown binding, or not this PSP's).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "No such callback endpoint.",
                  "error": {
                    "code": "not_found",
                    "message": "No such callback endpoint."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psp/transactions": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "submitPSPTransaction",
        "summary": "Submit a transaction as an originator PSP",
        "description": "One canonical request for every type an originating PSP sends (PSP-7.2): `sale` charges the source (payments pipeline), `payout` pays the destination from the available balance, `refund` refunds one of your own transactions (`original_transaction_id`). `cash_in` registers a cash deposit against a reference lookup (`lookup_id` from POST /v1/name-lookups, `method: \"cash_deposit\"`): it is the same engine as POST /v1/cash-deposits/intents (F2-7) \u2014 the same validation, pilot limits, bank confirmation (POST /v1/cash-deposits/intents/{id}/confirm) and `cash_in.*` events \u2014 and answers 202 with the intent (`intent_id`, `lookup_id`, `expires_at`); `originator_reference` becomes its `partner_reference`. One lookup is one deposit, from either endpoint, and the same Idempotency-Key on the second endpoint replays the first answer. A refused deposit is 422 `deposit_refused`. A type that completes at once answers 200 with its final result; one still running answers 202 with the KulPay ID (and a `Location`), and its outcome arrives on your webhooks (payment.*, payout.*, refund.*, cash_in.*). `originator_reference` is yours, unique per account and mode: the same reference under another Idempotency-Key is 409 `duplicate_originator_reference`. Records `transaction.created` (direction inbound, pattern inbound_request). Requires `transactions:write` plus the type's own scope: `payments:write`, `payouts:write` or `payments:refund` (a cash_in needs none more). Authentication is your secret key; OAuth client credentials and mTLS follow with PSP-8.7.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "originator_reference"
                ],
                "properties": {
                  "type": {
                    "enum": [
                      "sale",
                      "payout",
                      "refund",
                      "cash_in"
                    ]
                  },
                  "amount": {
                    "type": "string",
                    "description": "Decimal string. A refund without one refunds what is left."
                  },
                  "currency": {
                    "type": "string"
                  },
                  "method": {
                    "type": "string",
                    "description": "mpesa, emola, card, ..."
                  },
                  "source": {
                    "type": "object",
                    "description": "A sale's payer.",
                    "properties": {
                      "msisdn": {
                        "type": "string"
                      },
                      "card_token": {
                        "type": "string",
                        "description": "The acquirer's card token; a card number is refused with 400 `raw_card_data_not_accepted`."
                      }
                    }
                  },
                  "destination": {
                    "type": "object",
                    "description": "A payout's beneficiary.",
                    "properties": {
                      "msisdn": {
                        "type": "string"
                      },
                      "account": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      }
                    }
                  },
                  "original_transaction_id": {
                    "type": "string",
                    "description": "A refund's original sale."
                  },
                  "originator_reference": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "description": {
                    "type": "string"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "lookup_id": {
                    "type": "string",
                    "description": "A cash_in's reference lookup (POST /v1/name-lookups)."
                  },
                  "depositor_metadata": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "A cash_in's depositor details, as for POST /v1/cash-deposits/intents."
                  },
                  "beneficiary_metadata": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "A cash_in's beneficiary details, as for POST /v1/cash-deposits/intents."
                  }
                }
              },
              "example": {
                "type": "sale",
                "amount": "150.00",
                "currency": "MZN",
                "method": "mpesa",
                "source": {
                  "msisdn": "258841234567"
                },
                "originator_reference": "ORIG-1"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Final result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The KulPay ID."
                    },
                    "type": {
                      "enum": [
                        "sale",
                        "payout",
                        "refund",
                        "cash_in"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "final": {
                      "type": "boolean",
                      "description": "true: the status will not change (200); false: the outcome follows on your webhooks (202)."
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "originator_reference": {
                      "type": "string"
                    },
                    "original_transaction_id": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "intent_id": {
                      "type": "string",
                      "description": "A cash_in: the deposit intent (the same as id)."
                    },
                    "lookup_id": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "A cash_in: when the intent expires unconfirmed."
                    }
                  }
                },
                "example": {
                  "id": "3KWPqX2u4Kj2nLVEdBxmgvAdQC6",
                  "type": "refund",
                  "status": "succeeded",
                  "final": true,
                  "amount": "50",
                  "currency": "MZN",
                  "method": "mpesa",
                  "originator_reference": "ORIG-RF",
                  "original_transaction_id": "3KWPqSicHngSU8NiBPZ0jpFF1P7",
                  "created_at": "2026-10-10T22:15:41.467535573Z"
                }
              }
            }
          },
          "202": {
            "description": "Accepted and still running; the outcome follows on your webhooks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The KulPay ID."
                    },
                    "type": {
                      "enum": [
                        "sale",
                        "payout",
                        "refund",
                        "cash_in"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "final": {
                      "type": "boolean",
                      "description": "true: the status will not change (200); false: the outcome follows on your webhooks (202)."
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "originator_reference": {
                      "type": "string"
                    },
                    "original_transaction_id": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "intent_id": {
                      "type": "string",
                      "description": "A cash_in: the deposit intent (the same as id)."
                    },
                    "lookup_id": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "A cash_in: when the intent expires unconfirmed."
                    }
                  }
                },
                "example": {
                  "id": "3KWPqUxudtg7PGC2cVdwKXcBhpw",
                  "type": "sale",
                  "status": "pending",
                  "final": false,
                  "amount": "20",
                  "currency": "MZN",
                  "method": "mpesa",
                  "originator_reference": "ORIG-2",
                  "created_at": "2026-10-10T22:15:41.267450961Z"
                }
              }
            }
          },
          "400": {
            "description": "`idempotency_key_required`, `invalid_request` or an invalid amount.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "idempotency_key_required",
                  "detail": "This request moves money: send an Idempotency-Key header so a retry can never run it twice.",
                  "error": {
                    "code": "idempotency_key_required",
                    "message": "This request moves money: send an Idempotency-Key header so a retry can never run it twice."
                  }
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope` with `missing_scope`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: payments:write",
                  "missing_scope": "payments:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: payments:write",
                    "missing_scope": "payments:write"
                  }
                }
              }
            }
          },
          "404": {
            "description": "A refund of a transaction that is not yours.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "transaction not found",
                  "error": {
                    "code": "not_found",
                    "message": "transaction not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`duplicate_originator_reference` or `idempotency_key_reused`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "duplicate_originator_reference",
                  "detail": "A transaction with this originator_reference already exists; fetch it with GET /v1/psp/transactions?originator_reference=.",
                  "error": {
                    "code": "duplicate_originator_reference",
                    "message": "A transaction with this originator_reference already exists; fetch it with GET /v1/psp/transactions?originator_reference=."
                  }
                }
              }
            }
          },
          "422": {
            "description": "`not_supported`, `limit_exceeded`, `risk_declined`, `insufficient_balance` or `not_refundable`. `pilot_limit` while the account is a PSP in pilot: the live transaction breaks the pilot setting named in `rule` (`per_transaction`, `daily`, `monthly`, `currency`, `settings_missing`, or `merchant_not_allowed` for a sale whose `metadata.merchant_id` is missing or not on the allow-list).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not supported",
                  "status": 422,
                  "code": "not_supported",
                  "detail": "This transaction type is not available yet.",
                  "error": {
                    "code": "not_supported",
                    "message": "This transaction type is not available yet."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "findPSPTransactions",
        "summary": "Find a transaction by your reference",
        "parameters": [
          {
            "name": "originator_reference",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`: only transactions you submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "final": {
                            "type": "boolean"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "originator_reference": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPqSicHngSU8NiBPZ0jpFF1P7",
                      "type": "sale",
                      "status": "succeeded",
                      "final": true,
                      "amount": "150",
                      "currency": "MZN",
                      "method": "mpesa",
                      "originator_reference": "ORIG-1",
                      "created_at": "2026-10-10T22:15:41.050729989Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/psp/transactions/{id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getPSPTransaction",
        "summary": "A transaction you submitted",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The KulPay ID.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transaction.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The KulPay ID."
                    },
                    "type": {
                      "enum": [
                        "sale",
                        "payout",
                        "refund"
                      ]
                    },
                    "status": {
                      "type": "string"
                    },
                    "final": {
                      "type": "boolean",
                      "description": "true: the status will not change (200); false: the outcome follows on your webhooks (202)."
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "originator_reference": {
                      "type": "string"
                    },
                    "original_transaction_id": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPqSicHngSU8NiBPZ0jpFF1P7",
                  "type": "sale",
                  "status": "succeeded",
                  "final": true,
                  "amount": "150",
                  "currency": "MZN",
                  "method": "mpesa",
                  "originator_reference": "ORIG-1",
                  "created_at": "2026-10-10T22:15:41.050729989Z"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or not yours.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "transaction not found",
                  "error": {
                    "code": "not_found",
                    "message": "transaction not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{id}/reversals": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createReversal",
        "summary": "Reverse a transaction",
        "description": "Reverses a duplicated or uncertain transaction through the PSP's own reversal operation (M-Pesa reversal) with the original reference (PSP-6.8). A settled payment: the amount (default: what is left) is reserved, the ledger posts a reversing entry and the payment ends refunded or partially_refunded. A payment still in flight (its outcome uncertain) is reversed whole and ends cancelled. Started here by your own key it runs at once; started by an operator it waits for a second person. A provider without reversals answers 422 not_supported: refund instead. Events: reversal.requested, reversal.succeeded, reversal.failed (the last two are webhooks). Requires `transactions:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "amount": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "amount": "1250.00",
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The reversal (a refund of kind reversal).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "payment_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "refund_ref": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "deleted_at": {}
                  }
                },
                "example": {
                  "id": "3KUYZ1VKE7LXO4YAPRrEI8GCcM8",
                  "payment_id": "3KUYH1DSm7tFw4GIxZzMq8OkBu0",
                  "mode": "live",
                  "refund_ref": "MPS-REV-88123456",
                  "amount": "1250.75",
                  "currency": "MZN",
                  "amount_minor": 125075,
                  "status": "succeeded",
                  "reason": "Duplicate charge: the customer paid the same order twice",
                  "kind": "reversal",
                  "created_at": "2026-10-11T11:04:52Z",
                  "updated_at": "2026-10-11T11:04:55Z",
                  "deleted_at": null
                }
              }
            }
          },
          "404": {
            "description": "No such payment for this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "transaction not found",
                  "error": {
                    "code": "not_found",
                    "message": "transaction not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The payment cannot be reversed in its state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not reversible",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "payments: the payment cannot be reversed in its state",
                  "error": {
                    "code": "invalid_state",
                    "message": "payments: the payment cannot be reversed in its state"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`not_supported`, or the amount is more than what is left.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not supported",
                  "status": 422,
                  "code": "not_supported",
                  "detail": "the payment's provider does not support reversals",
                  "rule": "reversal",
                  "error": {
                    "code": "not_supported",
                    "message": "the payment's provider does not support reversals",
                    "rule": "reversal"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listReversals",
        "summary": "A transaction's reversals",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {},
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": null,
                  "total": 0
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{id}/stages": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "getTransactionStages",
        "summary": "Processing stages of a transaction",
        "description": "Each processing stage of one payment, in the order it ran, with `started_at`, `ended_at`, `duration_ms`, `outcome`, `reason_code`, the PSP's `raw_code`, KulPay's `mapped_code`, the `attempt` number and the `psp` that handled it: where the transaction stopped and why. Each stage also emits `transaction.stage.<stage>.<outcome>`. Stages and their outcomes: `receive`: `accepted`; `enrich_validate`: `passed`; `risk`: `skipped`, `approved`, `review_required`, `declined`, `challenged`; `route`: `routed`, `no_route`; `submit`: `accepted`, `requires_action`, `declined`, `soft_declined`, `error`, `unknown`; `await_confirmation`: `succeeded`, `failed`, `cancelled`, `expired`; `post`: `posted`; `notify`: `queued`, `skipped`; `settle`: `settled`; `reconcile`: `matched`, `exception`. A payment of another account is reported as not found. Requires `transactions:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The stages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TransactionStage"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "attempt": 1,
                      "duration_ms": 0,
                      "ended_at": "2026-10-10T22:15:45.71626081Z",
                      "id": "3KWPqx53WxvpSwvNu2c5fSulwx3",
                      "outcome": "accepted",
                      "payment_id": "3KWPqy64mjpEmk7YbiRaVXD1OFo",
                      "stage": "receive",
                      "started_at": "2026-10-10T22:15:45.71626081Z",
                      "transaction_type": "SALE"
                    },
                    {
                      "attempt": 1,
                      "duration_ms": 0,
                      "ended_at": "2026-10-10T22:15:45.71696009Z",
                      "id": "3KWPr3O87yjos0HPrldcBjR2kZG",
                      "outcome": "passed",
                      "payment_id": "3KWPqy64mjpEmk7YbiRaVXD1OFo",
                      "stage": "enrich_validate",
                      "started_at": "2026-10-10T22:15:45.71626081Z",
                      "transaction_type": "SALE"
                    }
                  ],
                  "total": 8
                }
              }
            }
          },
          "403": {
            "description": "Missing `transactions:read` (`insufficient_scope`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: transactions:read",
                  "missing_scope": "transactions:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: transactions:read",
                    "missing_scope": "transactions:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such payment for this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "transaction not found",
                  "error": {
                    "code": "not_found",
                    "message": "transaction not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{id}/timeline": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "getTransactionTimeline",
        "summary": "Unified timeline of a transaction",
        "description": "Every record about one payment in one ordered list: its events and its refunds' events, events recorded by the same requests, the requests themselves, provider callbacks, webhook deliveries and ledger postings. A payment of another account is reported as not found. Requires `transactions:read`. A cash deposit's id (its intent, the transaction the partner sees) returns its journey instead (IO-7.1): `{intent_id, lookup_id, correlation_id, deposit_id, status, outcome, final, items[]}`, each item `{at, stage, type, aggregate_type, aggregate_id, data}` in order: lookup (with the routing rule), intent, confirmation, credit, application at the resolver, suspense, reconciliation, outcome. The correlation_id is the lookup ID KulPay sends the resolver as X-Correlation-ID on every call.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "inbound, outbound or internal (PSP-7.1).",
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound",
                "internal"
              ]
            }
          },
          {
            "name": "pattern",
            "in": "query",
            "description": "The interaction pattern (PSP-7.1).",
            "schema": {
              "type": "string",
              "enum": [
                "outbound_sync",
                "outbound_async",
                "outbound_notification",
                "inbound_request",
                "inbound_callback",
                "inbound_file",
                "outbound_file",
                "status_query"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The timeline.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionTimeline"
                },
                "example": {
                  "transaction_id": "3KWPriUVlsoE66gvbeypObiNyg4",
                  "account_id": "3KWPrmSWzqQXwxG1hY9szJPTTrB",
                  "items": [
                    {
                      "id": "3KWPrluQuwW6HHzKcNJvGTNaCx1",
                      "at": "2026-10-10T22:15:51.590718Z",
                      "kind": "event",
                      "type": "payment.created",
                      "direction": "internal",
                      "actor": "system",
                      "outcome": "progressed",
                      "aggregate_id": "3KWPriUVlsoE66gvbeypObiNyg4",
                      "summary": "payment created",
                      "detail": {
                        "event_id": "3KWPrluQuwW6HHzKcNJvGTNaCx1",
                        "payload": {
                          "account_id": "3KWPrmSWzqQXwxG1hY9szJPTTrB",
                          "amount": "10",
                          "amount_minor": 1000,
                          "amount_refunded": "0",
                          "created_at": "2026-10-10T22:15:51.589301639Z",
                          "currency": "MZN",
                          "id": "3KWPriUVlsoE66gvbeypObiNyg4",
                          "method": "card",
                          "mode": "test",
                          "status": "created"
                        },
                        "schema_version": 2,
                        "sequence": 1
                      }
                    },
                    {
                      "id": "3KWPriUJUj7yaEBOuGvHzvOz9pH",
                      "at": "2026-10-10T22:15:51.592091Z",
                      "kind": "event",
                      "type": "transaction.stage.receive.accepted",
                      "direction": "internal",
                      "actor": "system",
                      "outcome": "progressed",
                      "duration_ms": 1,
                      "aggregate_id": "3KWPriUVlsoE66gvbeypObiNyg4",
                      "summary": "payment transaction.stage.receive.accepted",
                      "detail": {
                        "event_id": "3KWPriUJUj7yaEBOuGvHzvOz9pH",
                        "payload": {
                          "attempt": 1,
                          "duration_ms": 0,
                          "ended_at": "2026-10-10T22:15:51.571983101Z",
                          "id": "3KWPrmtasVjb4eSCE0ZPxh5pU0A",
                          "outcome": "accepted",
                          "payment_id": "3KWPriUVlsoE66gvbeypObiNyg4",
                          "stage": "receive",
                          "started_at": "2026-10-10T22:15:51.571983101Z",
                          "transaction_type": "SALE"
                        },
                        "schema_version": 1,
                        "sequence": 2
                      }
                    }
                  ],
                  "counts": {
                    "event": 15,
                    "ledger": 2,
                    "request": 1
                  },
                  "truncated": false
                }
              }
            }
          },
          "403": {
            "description": "Missing `transactions:read` (`insufficient_scope`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: transactions:read",
                  "missing_scope": "transactions:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: transactions:read",
                    "missing_scope": "transactions:read"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such transaction (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "transaction not found",
                  "error": {
                    "code": "not_found",
                    "message": "transaction not found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "No payments store (`timeline_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "timeline_unavailable",
                  "detail": "No payments store (timeline_unavailable).",
                  "error": {
                    "code": "timeline_unavailable",
                    "message": "No payments store (timeline_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/timeline": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "getAccountTimeline",
        "summary": "Timeline of a PSP or merchant account",
        "description": "The account's onboarding, configuration, credential and (as they are added) health events, oldest first; transactions are excluded. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Account id.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The timeline.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TimelineItem"
                      }
                    }
                  }
                },
                "example": {
                  "account_id": "3KWPsUwfH5am7QWL78hG1eA5hF8",
                  "items": [
                    {
                      "id": "3KWPsZjIhtYCYwYU9hJ9PF2fYXp",
                      "at": "2026-10-10T22:15:57.9868Z",
                      "kind": "event",
                      "type": "credential.api_key.created",
                      "direction": "internal",
                      "actor": "system",
                      "outcome": "progressed",
                      "aggregate_id": "3KWPsVR76rXSyH4FUHif8mucXKO",
                      "summary": "api_key credential.api_key.created",
                      "detail": {
                        "event_id": "3KWPsZjIhtYCYwYU9hJ9PF2fYXp",
                        "payload": {
                          "after": {
                            "account_id": "3KWPsUwfH5am7QWL78hG1eA5hF8",
                            "id": "3KWPsVR76rXSyH4FUHif8mucXKO",
                            "last4": "u7jc",
                            "mode": "test",
                            "name": "integration",
                            "scopes": [
                              "payments:read"
                            ]
                          },
                          "changed": [
                            "account_id",
                            "id"
                          ]
                        },
                        "schema_version": 1,
                        "sequence": 1
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such account (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "account not found",
                  "error": {
                    "code": "not_found",
                    "message": "account not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/chains": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "listAuditChains",
        "summary": "Audit chains and their verification state",
        "description": "Every chain's head and the result of the latest hourly verification. Requires `audit:read`.",
        "responses": {
          "200": {
            "description": "The chains.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditChain"
                      }
                    },
                    "broken": {
                      "type": "integer"
                    },
                    "unchained_events": {
                      "type": "integer",
                      "description": "Events committed but not yet placed on their chain by the relay."
                    }
                  }
                },
                "example": {
                  "broken": 0,
                  "data": [
                    {
                      "chain_id": "http",
                      "tenant_id": "",
                      "last_seq": 1,
                      "last_hash": "ec6955a4fc6f1a88c64ae3f15026d1f0eef653c392e98f5ed39fb1fbfbca69b9",
                      "updated_at": "2026-10-10T22:15:59.324259818Z",
                      "status": "ok",
                      "verified_seq": 0,
                      "last_run_at": "2026-10-10T22:15:59.217462565Z"
                    },
                    {
                      "chain_id": "tenant:3KWPsaD07P4cSnTR7riibX6S5GL",
                      "tenant_id": "3KWPsaD07P4cSnTR7riibX6S5GL",
                      "last_seq": 22,
                      "last_hash": "ab2e21369a7b9999bc9218e1106e047fb2663e84d09a0a702561cdd930771095",
                      "updated_at": "2026-10-10T22:15:59.15848143Z",
                      "status": "ok",
                      "verified_seq": 22,
                      "last_run_at": "2026-10-10T22:15:59.217462565Z"
                    }
                  ],
                  "unchained_events": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/chains/verify": {
      "post": {
        "tags": [
          "Audit"
        ],
        "operationId": "runAuditVerification",
        "summary": "Verify the chains now",
        "description": "Runs one pass of the hourly verification: every event added since the last verified position, its link to the previous one, and agreement with the latest anchor. A break emits `audit.chain.broken` and raises a critical alert. Requires `admin:write`.",
        "responses": {
          "200": {
            "description": "The pass.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "chains": {
                      "type": "integer"
                    },
                    "events_checked": {
                      "type": "integer"
                    },
                    "broken": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "broken": 0,
                  "chains": 3,
                  "events_checked": 33
                }
              }
            }
          },
          "503": {
            "description": "The verifier is not running (`audit_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "audit_unavailable",
                  "detail": "The verifier is not running (audit_unavailable).",
                  "error": {
                    "code": "audit_unavailable",
                    "message": "The verifier is not running (audit_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/anchors": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "listAuditAnchors",
        "summary": "Signed daily anchors",
        "description": "Newest first. Requires `audit:read`.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 366,
              "default": 31
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The anchors.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditAnchor"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPsi0wdFm856395f81eDLYC1O",
                      "day": "2026-10-10",
                      "merkle_root": "724afdde9b4b8d18d18253251e741ff49b2af7818d6cc95f9c0984b720126577",
                      "chains": 2,
                      "key_id": "audit-test",
                      "signed": true,
                      "created_at": "2026-10-10T22:15:59.199073083Z",
                      "document": {
                        "version": 1,
                        "day": "2026-10-10",
                        "generated_at": "2026-10-10T22:15:59.198Z",
                        "region": "mz",
                        "heads": [
                          {
                            "chain_id": "tenant:3KWPsaD07P4cSnTR7riibX6S5GL",
                            "last_seq": 22,
                            "last_hash": "ab2e21369a7b9999bc9218e1106e047fb2663e84d09a0a702561cdd930771095"
                          },
                          {
                            "chain_id": "tenant:3KWPsad9jYn6OMl14xjRYpHoPI2",
                            "last_seq": 11,
                            "last_hash": "8454fb004b4a911f5a6381ea0c641eb2269980edf5cf2b6f1eb2e5fdee4091d5"
                          }
                        ],
                        "merkle_root": "724afdde9b4b8d18d18253251e741ff49b2af7818d6cc95f9c0984b720126577",
                        "key_id": "audit-test"
                      },
                      "signature": "BH5wHDQIq8PocAswM+/fh0wso1EWnInEZ/jSWplUdWtii667KNhkuivk8ZEjJj875kReS2qAK/wK7WCc72+lAw==",
                      "public_key": "ANBaHR6iUTltVXr71FiLPG2Z2+uXL+0QoyVi6ibc3Po="
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad date or limit (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "from and to are dates (YYYY-MM-DD)",
                  "error": {
                    "code": "invalid_request",
                    "message": "from and to are dates (YYYY-MM-DD)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/public-key": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "getAuditPublicKey",
        "summary": "The audit signing key",
        "description": "The Ed25519 public key that signs anchors and export bundles. Pin it out of band and pass it to `kulpay-payments audit verify --public-key`. Requires `audit:read`.",
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "algorithm": {
                      "type": "string",
                      "example": "ed25519"
                    },
                    "key_id": {
                      "type": "string"
                    },
                    "public_key": {
                      "type": "string",
                      "description": "Base64."
                    }
                  }
                },
                "example": {
                  "algorithm": "ed25519",
                  "key_id": "audit-test",
                  "public_key": "ANBaHR6iUTltVXr71FiLPG2Z2+uXL+0QoyVi6ibc3Po="
                }
              }
            }
          },
          "404": {
            "description": "No signing key is configured (`audit_unsigned`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "audit_unsigned",
                  "detail": "No signing key is configured (audit_unsigned).",
                  "error": {
                    "code": "audit_unsigned",
                    "message": "No signing key is configured (audit_unsigned)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/audit/exports": {
      "post": {
        "tags": [
          "Audit"
        ],
        "operationId": "createAuditExport",
        "summary": "Export a signed audit bundle",
        "description": "Produces a ZIP for one tenant and period: `events.jsonl` and `events.csv` (the tenant's chain, contiguous), `http_audit.jsonl`, `anchors.json`, `manifest.json` (SHA-256 of every file) and `manifest.sig` (Ed25519). Verify it offline with `kulpay-payments audit verify bundle.zip --public-key <key>`. The export is recorded as `audit.export.created` on the tenant's chain. At most 92 days per bundle. Requires `audit:read`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tenant_id",
                  "from",
                  "to"
                ],
                "properties": {
                  "tenant_id": {
                    "type": "string",
                    "description": "Account id, or `platform`."
                  },
                  "from": {
                    "type": "string",
                    "description": "RFC 3339 time or YYYY-MM-DD (inclusive).",
                    "example": "2026-10-01"
                  },
                  "to": {
                    "type": "string",
                    "description": "RFC 3339 time or YYYY-MM-DD (exclusive).",
                    "example": "2026-10-08"
                  }
                }
              },
              "example": {
                "tenant_id": "3KWPsfhQUTnXIQuAYdECfQ9TDyp",
                "from": "2026-10-10T21:15:58Z",
                "to": "2026-10-10T23:15:58Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The bundle. `X-Audit-Export-Id` and `X-Audit-Events` describe it.",
            "content": {
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Missing tenant, bad or inverted range (`invalid_request`), or more than 92 days (`range_too_large`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "to must be after from",
                  "error": {
                    "code": "invalid_request",
                    "message": "to must be after from"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such tenant (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "tenant not found",
                  "error": {
                    "code": "not_found",
                    "message": "tenant not found"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Exports are not enabled (`audit_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "audit_unavailable",
                  "detail": "Exports are not enabled (audit_unavailable).",
                  "error": {
                    "code": "audit_unavailable",
                    "message": "Exports are not enabled (audit_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/jobs/{name}/run": {
      "post": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "runJobNow",
        "summary": "Run a job now",
        "description": "Makes the job due at once: the node leading it runs it on its next tick, with its lease and run record as usual (QA T-501). Requires `admin:write`.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Scheduled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "type": "string"
                    },
                    "scheduled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "job": "string",
                  "scheduled": true
                }
              }
            }
          },
          "404": {
            "description": "No job of that name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no job named nightly (GET /v1/admin/jobs lists them)",
                  "error": {
                    "code": "not_found",
                    "message": "no job named nightly (GET /v1/admin/jobs lists them)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/test/clock/advance": {
      "post": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "advanceTestClock",
        "summary": "Move a test deployment's time forward",
        "description": "In a deployment with test_apis on (never in live mode), moves the service's time forward by `duration` (a Go duration: 90m, 26h), so windows and deadlines pass without backdating rows (QA T-501). Elsewhere 404. Requires `admin:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "duration"
                ],
                "properties": {
                  "duration": {
                    "type": "string",
                    "example": "26h"
                  }
                }
              },
              "example": {
                "duration": "26h"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new time.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "now": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "description": "test_apis is off.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "payments: this deployment's clock cannot be moved (test_apis is off)",
                  "error": {
                    "code": "not_found",
                    "message": "payments: this deployment's clock cannot be moved (test_apis is off)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/jobs": {
      "get": {
        "tags": [
          "Event pipeline"
        ],
        "operationId": "listJobs",
        "summary": "Singleton jobs and their leaders",
        "description": "Settlement sync, retention, the audit anchor and the audit verifier run on one node per region. This lists each job, the node leading it and this node's run statistics; every run is also recorded as job.run.started and job.run.completed or job.run.failed. Requires `admin:read`.",
        "responses": {
          "200": {
            "description": "The jobs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "node": {
                      "type": "string",
                      "description": "The node that answered."
                    },
                    "region": {
                      "type": "string"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Job"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "job": "settlement_sync",
                      "every": "30s",
                      "leader_node": "node-a",
                      "lease_until": "2026-10-10T22:15:33.548606907Z",
                      "leader_alive": true,
                      "this_node_leads": true,
                      "runs_on_this_node": 1,
                      "failures_on_this_node": 0,
                      "last_run_at": "2026-10-10T22:15:13.567193217Z",
                      "last_success_at": "2026-10-10T22:15:13.567210404Z"
                    }
                  ],
                  "node": "node-a",
                  "region": "mz"
                }
              }
            }
          },
          "503": {
            "description": "No scheduler on this node (`jobs_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "jobs_unavailable",
                  "detail": "No scheduler on this node (jobs_unavailable).",
                  "error": {
                    "code": "jobs_unavailable",
                    "message": "No scheduler on this node (jobs_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/residency": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "getResidency",
        "summary": "Where this region's data lives",
        "description": "The residency report: this node's region, where each of its data stores is (database, event bus, object storage, audit anchors, backups, logs; credentials stripped), tenants per region in this database and the routing directory. Requires `admin:read`.",
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ResidencyReport"
                },
                "example": {
                  "region": "mz",
                  "base_url": "https://api.kulpay.co.mz",
                  "data_stores": [
                    {
                      "store": "database",
                      "location": "postgres://db.mz.internal:5432/kulpay",
                      "note": "tenant data, events, audit chains"
                    },
                    {
                      "store": "event bus",
                      "location": "nats://nats.mz.internal:4222"
                    }
                  ],
                  "tenants_by_region": {
                    "mz": 1,
                    "regional-sa": 1
                  },
                  "tenants_of_other_regions": 1,
                  "other_regions": {
                    "regional-sa": "https://sa.api.kulpay.com"
                  },
                  "directory_keys": 0
                }
              }
            }
          },
          "503": {
            "description": "Region pinning not configured (`residency_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "residency_unavailable",
                  "detail": "Region pinning not configured (residency_unavailable).",
                  "error": {
                    "code": "residency_unavailable",
                    "message": "Region pinning not configured (residency_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/applications": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "createPSPApplication",
        "summary": "Start a PSP application",
        "security": [],
        "description": "Creates a draft. No API key: the response carries a draft_token, the key to this draft. Also available as the onboarding portal at /onboarding.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PSPApplicationData"
              },
              "example": {
                "legal_name": "Banco Exemplo SA",
                "registration_number": "100234567",
                "country": "MZ",
                "licences": [
                  {
                    "type": "payment_institution",
                    "number": "BM-PI-0042",
                    "issuer": "Banco de Mo\u00e7ambique"
                  }
                ],
                "roles": [
                  "processor"
                ],
                "countries": [
                  "MZ"
                ],
                "currencies": [
                  "MZN"
                ],
                "transaction_types": [
                  "mobile_money_c2b"
                ],
                "expected_monthly_volume": 5000,
                "expected_monthly_value": {
                  "amount": "1000000",
                  "currency": "MZN"
                },
                "technical_contact": {
                  "name": "Ana",
                  "email": "ops@example.co.mz"
                },
                "compliance_contact": {
                  "name": "Rui",
                  "email": "ops@example.co.mz"
                },
                "language": "pt"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The draft, with what is still missing and its token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPApplication"
                },
                "example": {
                  "id": "3KWPlM2cQ5XsyMFrtw1MgS2gcZP",
                  "status": "draft",
                  "language": "pt",
                  "missing": [],
                  "data": {
                    "legal_name": "Banco Exemplo SA",
                    "registration_number": "100234567",
                    "country": "MZ",
                    "licences": [
                      {
                        "type": "payment_institution",
                        "number": "BM-PI-0042",
                        "issuer": "Banco de Mo\u00e7ambique"
                      }
                    ],
                    "roles": [
                      "processor"
                    ],
                    "countries": [
                      "MZ"
                    ],
                    "currencies": [
                      "MZN"
                    ],
                    "transaction_types": [
                      "mobile_money_c2b"
                    ],
                    "expected_monthly_volume": 5000,
                    "expected_monthly_value": {
                      "amount": "1000000",
                      "currency": "MZN"
                    },
                    "technical_contact": {
                      "name": "Ana",
                      "email": "ops@example.co.mz"
                    },
                    "compliance_contact": {
                      "name": "Rui",
                      "email": "ops@example.co.mz"
                    },
                    "language": "pt"
                  },
                  "draft_token": "pspapp_8eae512582741e86ae8eb28b3730d8012ed13c6c64f5e6e7"
                }
              }
            }
          },
          "400": {
            "description": "Malformed or unknown fields (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "Malformed or unknown fields (invalid_request).",
                  "error": {
                    "code": "invalid_request",
                    "message": "Malformed or unknown fields (invalid_request)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/applications/{id}": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getPSPApplication",
        "summary": "Read an application",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "KulPay-Application-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The application.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPApplication"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "draft",
                  "language": "pt",
                  "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "missing": [
                    "string"
                  ],
                  "data": {
                    "legal_name": "Electricidade de Mo\u00e7ambique",
                    "registration_number": "string",
                    "country": "MZ",
                    "licences": [
                      {
                        "type": "standard",
                        "number": "string",
                        "issuer": "string",
                        "expires_on": "2026-10-31"
                      }
                    ],
                    "roles": [
                      "processor"
                    ],
                    "countries": [
                      "string"
                    ],
                    "currencies": [
                      "string"
                    ],
                    "transaction_types": [
                      "string"
                    ],
                    "expected_monthly_volume": 12,
                    "expected_monthly_value": {
                      "amount": "1250.00",
                      "currency": "MZN"
                    },
                    "technical_contact": {
                      "name": "Electricidade de Mo\u00e7ambique",
                      "email": "ops@example.co.mz",
                      "phone": "+258841234567"
                    },
                    "compliance_contact": {
                      "name": "Electricidade de Mo\u00e7ambique",
                      "email": "ops@example.co.mz",
                      "phone": "+258841234567"
                    },
                    "language": "pt"
                  },
                  "draft_token": "tok_7Hq2\u2026"
                }
              }
            }
          },
          "404": {
            "description": "No such application, or the wrong token (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "application not found",
                  "error": {
                    "code": "not_found",
                    "message": "application not found"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "savePSPApplication",
        "summary": "Save the draft",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "KulPay-Application-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PSPApplicationData"
              },
              "example": {
                "legal_name": "Banco Exemplo SA",
                "registration_number": "100234567",
                "country": "MZ",
                "licences": [
                  {
                    "type": "payment_institution",
                    "number": "BM-PI-0042",
                    "issuer": "Banco de Mo\u00e7ambique"
                  }
                ],
                "roles": [
                  "processor"
                ],
                "countries": [
                  "MZ"
                ],
                "currencies": [
                  "MZN"
                ],
                "transaction_types": [
                  "mobile_money_c2b"
                ],
                "expected_monthly_volume": 5000,
                "expected_monthly_value": {
                  "amount": "1000000",
                  "currency": "MZN"
                },
                "technical_contact": {
                  "name": "Ana",
                  "email": "ops@example.co.mz"
                },
                "compliance_contact": {
                  "name": "Rui",
                  "email": "ops@example.co.mz"
                },
                "language": "pt"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The saved draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPApplication"
                },
                "example": {
                  "id": "3KWPqEOIKJcrcDVHzmltGAVUEqA",
                  "status": "draft",
                  "language": "pt",
                  "missing": [],
                  "data": {
                    "legal_name": "Banco Exemplo SA",
                    "registration_number": "100234567",
                    "country": "MZ",
                    "licences": [
                      {
                        "type": "payment_institution",
                        "number": "BM-PI-0042",
                        "issuer": "Banco de Mo\u00e7ambique"
                      }
                    ],
                    "roles": [
                      "processor"
                    ],
                    "countries": [
                      "MZ"
                    ],
                    "currencies": [
                      "MZN"
                    ],
                    "transaction_types": [
                      "mobile_money_c2b"
                    ],
                    "expected_monthly_volume": 5000,
                    "expected_monthly_value": {
                      "amount": "1000000",
                      "currency": "MZN"
                    },
                    "technical_contact": {
                      "name": "Ana",
                      "email": "ops@example.co.mz"
                    },
                    "compliance_contact": {
                      "name": "Rui",
                      "email": "ops@example.co.mz"
                    },
                    "language": "pt"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Already submitted (`already_submitted`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "already_submitted",
                  "detail": "Already submitted (already_submitted).",
                  "error": {
                    "code": "already_submitted",
                    "message": "Already submitted (already_submitted)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/applications/{id}/submit": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "submitPSPApplication",
        "summary": "Submit the application",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "KulPay-Application-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Creates the PSP in state applied as a tenant of the region its country belongs to, and sends a confirmation to its contacts in their language. Records psp.application.submitted and psp.applied.entered.",
        "responses": {
          "201": {
            "description": "Submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "region": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "psp_id": "3KWPlMun761v0d6dW1NtuPMxddi",
                  "region": "",
                  "status": "applied"
                }
              }
            }
          },
          "409": {
            "description": "Already submitted (`already_submitted`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "already_submitted",
                  "detail": "the application was already submitted",
                  "error": {
                    "code": "already_submitted",
                    "message": "the application was already submitted"
                  }
                }
              }
            }
          },
          "421": {
            "description": "The country is served by another region (`wrong_region`, with its base_url).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Misdirected Request",
                  "status": 421,
                  "code": "wrong_region",
                  "detail": "This tenant's data is held in region regional-sa; send the request to https://sa.api.kulpay.com.",
                  "base_url": "https://sa.api.kulpay.com",
                  "region": "regional-sa",
                  "error": {
                    "code": "wrong_region",
                    "message": "This tenant's data is held in region regional-sa; send the request to https://sa.api.kulpay.com.",
                    "base_url": "https://sa.api.kulpay.com",
                    "region": "regional-sa"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Required fields are missing; each is named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "application_incomplete",
                  "detail": "the application is missing required fields",
                  "missing": [
                    "registration_number",
                    "country"
                  ],
                  "error": {
                    "code": "application_incomplete",
                    "message": "the application is missing required fields",
                    "missing": [
                      "registration_number",
                      "country"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listPSPs",
        "summary": "List PSPs",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "PSPs, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PSP"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPcBhFkyaKP4fBYxzVtSYt15c",
                      "account_id": "3KWPcCVHpL1X5KnYNRFePUIhXba",
                      "application_id": "3KWPcAXW4vQOLny1X1Yt1ntiQ3F",
                      "kind": "lsp",
                      "legal_name": "\u00c1guas de Teste UX SA",
                      "registration_number": "UX-1791670427842",
                      "country": "MZ",
                      "region": "mz",
                      "language": "pt",
                      "status": "applied",
                      "kyb_status": "pending",
                      "state_entered_at": "2026-10-10T22:13:47.877114Z",
                      "contract_signed": false,
                      "created_at": "2026-10-10T22:13:47.877114Z",
                      "updated_at": "2026-10-10T22:13:47.877114Z"
                    },
                    {
                      "id": "3KWM6DmypQMkZuhc0w4xYsd0Up3",
                      "account_id": "3KWM6F2mypStePkHm3btPTMpk78",
                      "application_id": "3KWM6HfuaGcmeYDNvDyxniH4e21",
                      "kind": "lsp",
                      "legal_name": "\u00c1guas de Teste UX SA",
                      "registration_number": "UX-1791668693255",
                      "country": "MZ",
                      "region": "mz",
                      "language": "pt",
                      "status": "applied",
                      "kyb_status": "pending",
                      "state_entered_at": "2026-10-10T21:44:53.291305Z",
                      "contract_signed": false,
                      "created_at": "2026-10-10T21:44:53.291305Z",
                      "updated_at": "2026-10-10T21:44:53.291305Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getPSP",
        "summary": "A PSP and its lifecycle",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "The PSP, every transition with actor and reason, seconds spent in each state, the transitions allowed now, the pilot gates and the 1-day clock (seconds since KYB approval, to live). Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp": {
                      "$ref": "#/components/schemas/PSP"
                    },
                    "transitions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "from": {
                            "type": "string"
                          },
                          "to": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "actor": {
                            "type": "string"
                          },
                          "at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "seconds_in_previous_state": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "seconds_in_state": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    },
                    "allowed_transitions": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "pilot_gates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "met": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "onboarding_seconds": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "psp": {
                    "id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                    "account_id": "3KWPpsYfFNrQEhm84v3anWxHrHj",
                    "application_id": "3KWPprbA3vQfJCZgfhOvbGOxKhO",
                    "kind": "psp",
                    "legal_name": "Banco Exemplo SA",
                    "registration_number": "100234567",
                    "country": "MZ",
                    "region": "mz",
                    "language": "pt",
                    "status": "live",
                    "risk_tier": "medium",
                    "kyb_status": "approved",
                    "state_entered_at": "2026-10-18T22:15:36.187945473Z",
                    "live_at": "2026-10-18T22:15:36.187945473Z",
                    "next_review_at": "2027-10-10T22:15:36.187945473Z",
                    "contract_signed": true,
                    "sandbox_enabled_at": "2026-10-10T22:15:36.187945473Z",
                    "sandbox_key_id": "3KWPps54CGNg8OL8aTqvdGqMbZb",
                    "certified_at": "2026-10-10T22:15:36.187945473Z",
                    "pilot_started_at": "2026-10-10T22:15:36.187945473Z",
                    "live_key_id": "3KWPpr5y02e3qlgVBBep5PQmDrF",
                    "created_at": "2026-10-10T22:15:36.187945473Z",
                    "updated_at": "2026-10-18T22:15:36.187945473Z"
                  },
                  "transitions": [
                    {
                      "id": "3KWPpsw0BPdDgcswkkkys6ADGv2",
                      "psp_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                      "from": "applied",
                      "to": "kyb_review",
                      "reason": "start",
                      "actor": "operator:ana",
                      "at": "2026-10-10T22:15:36.187945473Z",
                      "seconds_in_previous_state": 0
                    },
                    {
                      "id": "3KWPptE2FUaoKRJWuQ3s6AyWjt4",
                      "psp_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                      "from": "",
                      "to": "applied",
                      "reason": "application submitted",
                      "actor": "applicant",
                      "at": "2026-10-10T22:15:36.187945473Z",
                      "seconds_in_previous_state": 0
                    }
                  ],
                  "seconds_in_state": {
                    "applied": 0,
                    "certification": 0,
                    "kyb_review": 0,
                    "live": 0,
                    "pilot": 691200,
                    "technical_setup": 0
                  },
                  "allowed_transitions": [
                    "offboarding",
                    "suspended"
                  ],
                  "pilot_gates": [
                    {
                      "name": "kyb_approved",
                      "met": true
                    },
                    {
                      "name": "contract_signed",
                      "met": true
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/transitions": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "transitionPSP",
        "summary": "Move a PSP in its lifecycle",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Only transitions in the declared table, with their gates met, are allowed; each records the actor and the reason and emits psp.<state>.entered. A refused one emits psp.transition.rejected. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "to",
                  "reason"
                ],
                "properties": {
                  "to": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              },
              "example": {
                "to": "kyb_review",
                "reason": "go"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PSP after the move.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                  "account_id": "3KWPpsYfFNrQEhm84v3anWxHrHj",
                  "application_id": "3KWPprbA3vQfJCZgfhOvbGOxKhO",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "mz",
                  "language": "pt",
                  "status": "pilot",
                  "risk_tier": "medium",
                  "kyb_status": "approved",
                  "state_entered_at": "2026-10-10T22:15:36.187945473Z",
                  "next_review_at": "2027-10-10T22:15:36.187945473Z",
                  "contract_signed": true,
                  "sandbox_enabled_at": "2026-10-10T22:15:36.187945473Z",
                  "sandbox_key_id": "3KWPps54CGNg8OL8aTqvdGqMbZb",
                  "certified_at": "2026-10-10T22:15:36.187945473Z",
                  "pilot_started_at": "2026-10-10T22:15:36.187945473Z",
                  "live_key_id": "3KWPpr5y02e3qlgVBBep5PQmDrF",
                  "created_at": "2026-10-10T22:15:36.187945473Z",
                  "updated_at": "2026-10-10T22:15:36.187945473Z"
                }
              }
            }
          },
          "409": {
            "description": "Not allowed (`invalid_transition`, with from, to and cause).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_transition",
                  "detail": "cannot move PSP from technical_setup to suspended: suspension needs two people (POST /v1/admin/psps/{id}/suspension) or an emergency suspension",
                  "cause": "suspension needs two people (POST /v1/admin/psps/{id}/suspension) or an emergency suspension",
                  "from": "technical_setup",
                  "to": "suspended",
                  "error": {
                    "code": "invalid_transition",
                    "message": "cannot move PSP from technical_setup to suspended: suspension needs two people (POST /v1/admin/psps/{id}/suspension) or an emergency suspension",
                    "cause": "suspension needs two people (POST /v1/admin/psps/{id}/suspension) or an emergency suspension",
                    "from": "technical_setup",
                    "to": "suspended"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/applications/{id}/documents": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "uploadApplicationDocument",
        "summary": "Upload a KYB document (applicant)",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "KulPay-Application-Token",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "file"
                ],
                "properties": {
                  "type": {
                    "enum": [
                      "licence",
                      "registration_certificate",
                      "articles",
                      "ubo_declaration",
                      "director_ids",
                      "aml_policy",
                      "financial_statements"
                    ]
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Up to 10 MB."
                  }
                }
              }
            }
          }
        },
        "description": "After submission, the applicant uploads its KYB documents with its draft token. Stored encrypted in the region's object storage with a checksum; records psp.kyb.document.uploaded.",
        "responses": {
          "201": {
            "description": "The document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPDocument"
                },
                "example": {
                  "id": "3KWPq3VocBtK5YJsaPSzJXtujyD",
                  "psp_id": "3KWPpxo3jWfN94PGtqWEXcMLDgp",
                  "type": "licence",
                  "file_id": "3KWPq4JEwiZkjmlc4D4BQL6xx0j",
                  "filename": "licence.pdf",
                  "sha256": "4cff4e3de93bfed4899f4cbfc7be8b77cdafec9560a995cd6092c00c8ba75302",
                  "status": "pending",
                  "uploaded_at": "2026-10-10T22:15:37.433724445Z"
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "application not found",
                  "error": {
                    "code": "not_found",
                    "message": "application not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not submitted yet (`not_submitted`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_submitted",
                  "detail": "Not submitted yet (not_submitted).",
                  "error": {
                    "code": "not_submitted",
                    "message": "Not submitted yet (not_submitted)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getKYB",
        "summary": "KYB review state",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Checklist (documents by type, sanctions and PEP screening, risk tier, blockers, pending approvals), documents, screenings and the risk-tier defaults. Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The review.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "checklist": {
                    "documents": {
                      "aml_policy": "missing",
                      "articles": "missing",
                      "director_ids": "missing",
                      "financial_statements": "missing",
                      "licence": "missing",
                      "registration_certificate": "missing",
                      "ubo_declaration": "missing"
                    },
                    "screenings": {
                      "pep": "missing",
                      "sanctions": "missing"
                    },
                    "risk_tier": "",
                    "ready_for_approval": false,
                    "blockers": [
                      "document licence: missing",
                      "document registration_certificate: missing"
                    ],
                    "pending_approvals": []
                  },
                  "documents": [],
                  "risk_tiers": {
                    "high": {
                      "review_months": 6,
                      "per_transaction_limit": "100000",
                      "daily_limit": "5000000",
                      "monthly_limit": "100000000",
                      "currency": "MZN"
                    },
                    "low": {
                      "review_months": 24,
                      "per_transaction_limit": "1000000",
                      "daily_limit": "50000000",
                      "monthly_limit": "1000000000",
                      "currency": "MZN"
                    },
                    "medium": {
                      "review_months": 12,
                      "per_transaction_limit": "500000",
                      "daily_limit": "20000000",
                      "monthly_limit": "400000000",
                      "currency": "MZN"
                    }
                  },
                  "screenings": []
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/documents": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "adminUploadKYBDocument",
        "summary": "Upload a KYB document (operator)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "file"
                ],
                "properties": {
                  "type": {
                    "enum": [
                      "licence",
                      "registration_certificate",
                      "articles",
                      "ubo_declaration",
                      "director_ids",
                      "aml_policy",
                      "financial_statements"
                    ]
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Up to 10 MB."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPDocument"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "type": "standard",
                  "file_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "filename": "Electricidade de Mo\u00e7ambique",
                  "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "status": "pending",
                  "review_comment": "string",
                  "reviewed_by": "string",
                  "reviewed_at": "2026-10-10T09:30:00Z",
                  "uploaded_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/documents/{doc}": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "downloadKYBDocument",
        "summary": "Download a KYB document",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "doc",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Decrypted, after its content is checked against its checksum (X-Content-SHA256). Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "409": {
            "description": "The stored document does not match its checksum (`document_tampered`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "document_tampered",
                  "detail": "The stored document does not match its checksum (document_tampered).",
                  "error": {
                    "code": "document_tampered",
                    "message": "The stored document does not match its checksum (document_tampered)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/documents/{doc}/review": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "reviewKYBDocument",
        "summary": "Accept or reject a document",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "doc",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision"
                ],
                "properties": {
                  "decision": {
                    "enum": [
                      "accepted",
                      "rejected"
                    ]
                  },
                  "comment": {
                    "type": "string",
                    "description": "Required when rejecting."
                  }
                }
              },
              "example": {
                "decision": "accepted"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPDocument"
                },
                "example": {
                  "id": "3KWPq3VocBtK5YJsaPSzJXtujyD",
                  "psp_id": "3KWPpxo3jWfN94PGtqWEXcMLDgp",
                  "type": "licence",
                  "file_id": "3KWPq4JEwiZkjmlc4D4BQL6xx0j",
                  "filename": "licence.pdf",
                  "sha256": "4cff4e3de93bfed4899f4cbfc7be8b77cdafec9560a995cd6092c00c8ba75302",
                  "status": "accepted",
                  "reviewed_by": "key_accounts",
                  "reviewed_at": "2026-10-10T22:15:37.820822651Z",
                  "uploaded_at": "2026-10-10T22:15:37.433724445Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/screenings": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "recordScreening",
        "summary": "Record a manual sanctions or PEP check",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "subject",
                  "result"
                ],
                "properties": {
                  "kind": {
                    "enum": [
                      "sanctions",
                      "pep"
                    ]
                  },
                  "subject": {
                    "type": "string"
                  },
                  "result": {
                    "enum": [
                      "clear",
                      "hit"
                    ]
                  },
                  "reference": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "evidence_file_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "kind": "sanctions",
                "subject": "Banco Exemplo SA",
                "result": "clear",
                "reference": "UN, EU, OFAC lists 2026-10-08"
              }
            }
          }
        },
        "description": "Stores the check with its evidence; emits psp.screening.completed and, for a hit, psp.screening.hit.",
        "responses": {
          "201": {
            "description": "The screening.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPScreening"
                },
                "example": {
                  "id": "3KWPq13Jp78LpXfEc3b4aNkTdSe",
                  "psp_id": "3KWPpxo3jWfN94PGtqWEXcMLDgp",
                  "kind": "sanctions",
                  "subject": "Banco Exemplo SA",
                  "provider": "manual",
                  "result": "clear",
                  "reference": "UN, EU, OFAC lists 2026-10-08",
                  "performed_by": "key_accounts",
                  "at": "2026-10-10T22:15:37.992225153Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/screenings/run": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "runScreening",
        "summary": "Screen with a provider",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "provider"
                ],
                "properties": {
                  "provider": {
                    "type": "string",
                    "description": "manual (creates review tasks) or a configured provider."
                  }
                }
              },
              "example": {
                "provider": "mpesa"
              }
            }
          }
        },
        "description": "Screens the PSP and its contacts for sanctions and PEP with the provider (PSP-4.4); every result is stored as evidence and emitted.",
        "responses": {
          "201": {
            "description": "The screenings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PSPScreening"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                      "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                      "kind": "sanctions",
                      "subject": "string",
                      "provider": "mpesa",
                      "result": "clear",
                      "reference": "2003014567",
                      "notes": "Pagamento de energia, outubro",
                      "evidence_file_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                      "performed_by": "string",
                      "at": "2026-10-10T09:30:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/risk-tier": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "setRiskTier",
        "summary": "Set the risk tier",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tier",
                  "reason"
                ],
                "properties": {
                  "tier": {
                    "enum": [
                      "low",
                      "medium",
                      "high"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "tier": "medium",
                "reason": "profile"
              }
            }
          }
        },
        "description": "Sets default limits and the review frequency (high 6 months, medium 12, low 24).",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                  "account_id": "3KWPpklQ3UMKtjqz5XSeoWPYWKh",
                  "application_id": "3KWPpo6ixiv7LczATPXP4CQXpS6",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "applied",
                  "risk_tier": "medium",
                  "kyb_status": "pending",
                  "state_entered_at": "2026-10-10T22:15:35.185876129Z",
                  "next_review_at": "2027-10-10T22:15:35.218295061Z",
                  "contract_signed": false,
                  "created_at": "2026-10-10T22:15:35.185876129Z",
                  "updated_at": "2026-10-10T22:15:35.218295061Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/kyb/decision": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "kybDecision",
        "summary": "Decide KYB",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision",
                  "reason"
                ],
                "properties": {
                  "decision": {
                    "enum": [
                      "approve",
                      "reject",
                      "request_info"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "missing": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "decision": "approve",
                "reason": "complete"
              }
            }
          }
        },
        "description": "approve prepares a maker-checker request (202) that a different officer decides; reject rejects KYB and the PSP; request_info emails the PSP what is missing and moves it to info_requested.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "legal_name": "Electricidade de Mo\u00e7ambique",
                  "registration_number": "string",
                  "country": "MZ",
                  "region": "string",
                  "language": "pt",
                  "status": "applied",
                  "kyb_status": "pending",
                  "resume_state": "succeeded",
                  "risk_tier": "string",
                  "state_entered_at": "2026-10-10T09:30:00Z",
                  "kyb_approved_at": "2026-10-10T09:30:00Z",
                  "live_at": "2026-10-10T09:30:00Z",
                  "contract_ref": "string",
                  "contract_signed": true,
                  "sandbox_enabled_at": "2026-10-10T09:30:00Z",
                  "sandbox_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "certified_at": "2026-10-10T09:30:00Z",
                  "certification_run_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "pilot_started_at": "2026-10-10T09:30:00Z",
                  "live_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "last_review_at": "2026-10-10T09:30:00Z",
                  "review_notice": "",
                  "emergency_suspended_at": "2026-10-10T09:30:00Z",
                  "suspension_review_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "suspension_review_overdue": true
                }
              }
            }
          },
          "202": {
            "description": "Approval prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPq4nYTn5iKz3zaFVWv7HBAsO",
                    "kind": "psp.kyb.approve",
                    "subject_id": "3KWPpxo3jWfN94PGtqWEXcMLDgp",
                    "tenant_id": "3KWPq2SN77xFNSG3RWceD51rpAl",
                    "reason": "complete",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:38.073385131Z"
                  },
                  "message": "KYB approval prepared; a second compliance officer must approve it"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/approvals": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listApprovals",
        "summary": "Maker-checker requests",
        "parameters": [
          {
            "name": "subject_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "pending",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Requests.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApprovalRequest"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KVi1gXAMzy3tK5Mb3fibPDDsSL",
                      "kind": "bill.biller_status",
                      "subject_id": "3KVi1cUPS01yJkDC7pITrc8G4WQ",
                      "tenant_id": "platform",
                      "payload": "{\"status\":\"active\"}",
                      "reason": "playground sandbox",
                      "prepared_by": "f5ed8d5b44ff381e41d910db872c64ac",
                      "status": "approved",
                      "decided_by": "9c65c40c72be831e4987292492da6bfa",
                      "decision_reason": "playground sandbox",
                      "created_at": "2026-10-10T16:15:22.005996Z",
                      "decided_at": "2026-10-10T16:15:22.021089Z"
                    },
                    {
                      "id": "3KVi1aqsDaTfwtvqG8RGBmMf6LO",
                      "kind": "bill.biller_status",
                      "subject_id": "3KVi1arnE6Mzj1j3PjS3XKxQsdq",
                      "tenant_id": "platform",
                      "payload": "{\"status\":\"active\"}",
                      "reason": "playground sandbox",
                      "prepared_by": "f5ed8d5b44ff381e41d910db872c64ac",
                      "status": "approved",
                      "decided_by": "9c65c40c72be831e4987292492da6bfa",
                      "decision_reason": "playground sandbox",
                      "created_at": "2026-10-10T16:15:21.927802Z",
                      "decided_at": "2026-10-10T16:15:21.943088Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/approvals/{id}/decide": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "decideApproval",
        "summary": "Approve or reject a request",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "approve"
                ],
                "properties": {
                  "approve": {
                    "type": "boolean"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "approve": true,
                "reason": "ok"
              }
            }
          }
        },
        "description": "The checker must not be the person who prepared it. Approval applies the decision in the same transaction. Requires `accounts:write`.",
        "responses": {
          "200": {
            "description": "The request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovalRequest"
                },
                "example": {
                  "id": "3KWPkwtupluYYLGAw7At48TbMyG",
                  "kind": "bill.biller_status",
                  "subject_id": "3KWPkylzKmRsF6Rq3xef8pjun2x",
                  "tenant_id": "platform",
                  "payload": "{\"status\":\"active\"}",
                  "reason": "contract",
                  "prepared_by": "key_accounts",
                  "status": "approved",
                  "decided_by": "key_checker",
                  "decision_reason": "ok",
                  "created_at": "2026-10-10T22:14:57.89256229Z",
                  "decided_at": "2026-10-10T22:14:57.919880851Z"
                }
              }
            }
          },
          "403": {
            "description": "`same_approver`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "same_approver",
                  "detail": "payments: the person who prepared a request cannot approve it",
                  "error": {
                    "code": "same_approver",
                    "message": "payments: the person who prepared a request cannot approve it"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`already_decided`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "already_decided",
                  "detail": "already_decided",
                  "error": {
                    "code": "already_decided",
                    "message": "already_decided"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/configs": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listConfigVersions",
        "summary": "Configuration versions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "enum": [
                "capability_matrix",
                "orientation_profile",
                "routing",
                "retry_policy",
                "commercial_terms",
                "statement_profile",
                "technical_config"
              ]
            }
          }
        ],
        "description": "Every version of every configuration kind (or one kind), newest first. Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "Versions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConfigVersion"
                      }
                    },
                    "kinds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPpkZNsNRnKt4lDIVa7a0ns7t",
                      "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                      "kind": "commercial_terms",
                      "version": 3,
                      "status": "pending",
                      "reason": "limit too low",
                      "based_on": 1,
                      "approval_id": "3KWPplZqDOrQjHFV064vY3MD58T",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:35.427494098Z",
                      "document": {
                        "contract_ref": "KP-2026-014",
                        "fee_schedule": [
                          {
                            "currency": "MZN",
                            "fixed": "0",
                            "method": "mpesa",
                            "percent": "1.5"
                          }
                        ],
                        "funds_flow": "collecting",
                        "limits": {
                          "currency": "MZN",
                          "daily": "1000000",
                          "monthly": "20000000",
                          "per_transaction": "400000"
                        },
                        "settlement": {
                          "account": "BCI 0001",
                          "currency": "MZN",
                          "frequency_days": 1
                        }
                      }
                    },
                    {
                      "id": "3KWPphHmJysI9m55s7C6dpxyJ7p",
                      "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                      "kind": "commercial_terms",
                      "version": 2,
                      "status": "active",
                      "reason": "setup",
                      "approval_id": "3KWPpjjLsFZT3cq9khF4WJcFINP",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:35.322544516Z",
                      "activated_at": "2026-10-10T22:15:35.350756793Z",
                      "document": {
                        "contract_ref": "KP-2026-014",
                        "fee_schedule": [
                          {
                            "currency": "MZN",
                            "fixed": "0",
                            "method": "mpesa",
                            "percent": "1.5"
                          }
                        ],
                        "funds_flow": "collecting",
                        "limits": {
                          "currency": "MZN",
                          "daily": "1000000",
                          "monthly": "20000000",
                          "per_transaction": "300000"
                        },
                        "settlement": {
                          "account": "BCI 0001",
                          "currency": "MZN",
                          "frequency_days": 1
                        }
                      }
                    }
                  ],
                  "kinds": [
                    "capability_matrix",
                    "orientation_profile"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/configs/{kind}": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "proposeConfigVersion",
        "summary": "Create a configuration version",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "enum": [
                "capability_matrix",
                "orientation_profile",
                "routing",
                "retry_policy",
                "commercial_terms",
                "statement_profile",
                "technical_config"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document",
                  "reason"
                ],
                "properties": {
                  "document": {
                    "type": "object"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "document": {
                  "contract_ref": "KP-2026-014",
                  "fee_schedule": [
                    {
                      "method": "mpesa",
                      "percent": "1.5",
                      "fixed": "0",
                      "currency": "MZN"
                    }
                  ],
                  "funds_flow": "collecting",
                  "settlement": {
                    "frequency_days": 1,
                    "currency": "MZN",
                    "account": "BCI 0001"
                  },
                  "limits": {
                    "per_transaction": "400000",
                    "daily": "1000000",
                    "monthly": "20000000",
                    "currency": "MZN"
                  }
                },
                "reason": "setup"
              }
            }
          }
        },
        "description": "Creates the next version and records config.<kind>.version_created (and psp.commercial_terms.version_created). commercial_terms, retry_policy (a change to a money-moving pattern) and capability_matrix need a second person: the version stays pending with a config.activate approval request (202) until approved, then becomes active and supersedes the previous one (config.<kind>.activated). Other kinds are active at once (201). Only one version of a kind may be pending. Requires `accounts:write`. The routing kind is retired: 410 `config_kind_retired`. technical_config has the sections endpoints, credentials, webhooks and ip_allowlist, each checked as the onboarding portal checks it (https URLs and no private addresses, PEM certificates and PGP keys that parse, public CIDRs no broader than /24, catalogue event types); a failure is a 400 naming section, check and detail. Endpoint reachability over TLS is probed in the background and recorded on the setup step (psp.technical_step.probed).",
        "responses": {
          "201": {
            "description": "Active at once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigProposed"
                },
                "example": {
                  "version": {
                    "id": "3KWPpkrJxAYv4GusdsaY9P0DQj5",
                    "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                    "kind": "technical_config",
                    "version": 1,
                    "status": "active",
                    "reason": "setup",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:35.380789122Z",
                    "activated_at": "2026-10-10T22:15:35.38296785Z"
                  }
                }
              }
            }
          },
          "202": {
            "description": "Waiting for approval.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigProposed"
                },
                "example": {
                  "approval": {
                    "id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                    "kind": "config.activate",
                    "subject_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                    "tenant_id": "3KWPpklQ3UMKtjqz5XSeoWPYWKh",
                    "payload": "{\"version_id\":\"3KWPpmre6hzgOpcO2dDy9gATCiE\",\"kind\":\"commercial_terms\",\"version\":1}",
                    "reason": "commercial_terms v1: setup",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:35.241837569Z"
                  },
                  "message": "version prepared; a second person must approve it before it is active",
                  "version": {
                    "id": "3KWPpmre6hzgOpcO2dDy9gATCiE",
                    "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                    "kind": "commercial_terms",
                    "version": 1,
                    "status": "pending",
                    "reason": "setup",
                    "approval_id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:35.240082387Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid document (`invalid_request`), e.g. commercial terms above the risk tier, or a technical_config section that fails a check: `technical config: endpoints: callback URL: must use https`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "commercial terms: set the risk tier before commercial terms: limits may not exceed its defaults",
                  "error": {
                    "code": "invalid_request",
                    "message": "commercial terms: set the risk tier before commercial terms: limits may not exceed its defaults"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          },
          "410": {
            "description": "The kind is retired (`config_kind_retired`): routing, which the router never read, is configured in the global routing rules. The body names `kind` and `successor`; the `Link` header (rel=\"successor-version\") points to it. Versions of a retired kind stay readable.",
            "headers": {
              "Link": {
                "description": "Where the setting lives now: `</v1/routing/versions>; rel=\"successor-version\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Configuration kind retired",
                  "status": 410,
                  "code": "config_kind_retired",
                  "detail": "The routing configuration kind is retired; its versions stay readable. Configure it at /v1/routing/versions.",
                  "kind": "routing",
                  "successor": "/v1/routing/versions",
                  "error": {
                    "code": "config_kind_retired",
                    "message": "The routing configuration kind is retired; its versions stay readable. Configure it at /v1/routing/versions."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/configs/{kind}/diff": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "diffConfigVersions",
        "summary": "Compare two versions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "enum": [
                "capability_matrix",
                "orientation_profile",
                "routing",
                "retry_policy",
                "commercial_terms",
                "statement_profile",
                "technical_config"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "description": "Field-by-field differences by JSON path (e.g. limits.daily, fee_schedule[0].percent).",
        "responses": {
          "200": {
            "description": "Changes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "from": {
                      "type": "integer"
                    },
                    "to": {
                      "type": "integer"
                    },
                    "changes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "change": {
                            "enum": [
                              "added",
                              "removed",
                              "changed"
                            ]
                          },
                          "from": {},
                          "to": {}
                        }
                      }
                    }
                  }
                },
                "example": {
                  "changes": [
                    {
                      "path": "limits.per_transaction",
                      "change": "changed",
                      "from": "400000",
                      "to": "300000"
                    }
                  ],
                  "from": 1,
                  "to": 2
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/configs/{kind}/{version}": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getConfigVersion",
        "summary": "One configuration version",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "enum": [
                "capability_matrix",
                "orientation_profile",
                "routing",
                "retry_policy",
                "commercial_terms",
                "statement_profile",
                "technical_config"
              ]
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The version with its document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigVersion"
                },
                "example": {
                  "id": "3KWPpmre6hzgOpcO2dDy9gATCiE",
                  "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                  "kind": "commercial_terms",
                  "version": 1,
                  "status": "superseded",
                  "reason": "setup",
                  "approval_id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                  "created_by": "key_accounts",
                  "created_at": "2026-10-10T22:15:35.240082387Z",
                  "activated_at": "2026-10-10T22:15:35.281788234Z",
                  "document": {
                    "contract_ref": "KP-2026-014",
                    "fee_schedule": [
                      {
                        "currency": "MZN",
                        "fixed": "0",
                        "method": "mpesa",
                        "percent": "1.5"
                      }
                    ],
                    "funds_flow": "collecting",
                    "limits": {
                      "currency": "MZN",
                      "daily": "1000000",
                      "monthly": "20000000",
                      "per_transaction": "400000"
                    },
                    "settlement": {
                      "account": "BCI 0001",
                      "currency": "MZN",
                      "frequency_days": 1
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "configuration version not found",
                  "error": {
                    "code": "not_found",
                    "message": "configuration version not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/configs/{kind}/{version}/rollback": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "rollbackConfigVersion",
        "summary": "Roll back to a version",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "enum": [
                "capability_matrix",
                "orientation_profile",
                "routing",
                "retry_policy",
                "commercial_terms",
                "statement_profile",
                "technical_config"
              ]
            }
          },
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "limit too low"
              }
            }
          }
        },
        "description": "Creates a new version copying the given one (based_on), through the same approval path as any change.",
        "responses": {
          "201": {
            "description": "Active at once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigProposed"
                },
                "example": {
                  "version": {
                    "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "kind": "capability_matrix",
                    "version": 1,
                    "status": "pending",
                    "reason": "Pagamento de energia, outubro",
                    "based_on": 1,
                    "approval_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "created_by": "string",
                    "created_at": "2026-10-10T09:30:00Z",
                    "activated_at": "2026-10-10T09:30:00Z",
                    "document": {}
                  },
                  "approval": {
                    "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "kind": "standard",
                    "subject_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "reason": "Pagamento de energia, outubro",
                    "prepared_by": "string",
                    "status": "pending",
                    "decided_by": "string",
                    "decision_reason": "Pagamento de energia, outubro",
                    "created_at": "2026-10-10T09:30:00Z",
                    "decided_at": "2026-10-10T09:30:00Z"
                  },
                  "message": "Pagamento de energia, outubro"
                }
              }
            }
          },
          "202": {
            "description": "Waiting for approval.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigProposed"
                },
                "example": {
                  "approval": {
                    "id": "3KWPplZqDOrQjHFV064vY3MD58T",
                    "kind": "config.activate",
                    "subject_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                    "tenant_id": "3KWPpklQ3UMKtjqz5XSeoWPYWKh",
                    "payload": "{\"version_id\":\"3KWPpkZNsNRnKt4lDIVa7a0ns7t\",\"kind\":\"commercial_terms\",\"version\":3}",
                    "reason": "commercial_terms v3: limit too low",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:35.429536568Z"
                  },
                  "message": "version prepared; a second person must approve it before it is active",
                  "version": {
                    "id": "3KWPpkZNsNRnKt4lDIVa7a0ns7t",
                    "psp_id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                    "kind": "commercial_terms",
                    "version": 3,
                    "status": "pending",
                    "reason": "limit too low",
                    "based_on": 1,
                    "approval_id": "3KWPplZqDOrQjHFV064vY3MD58T",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:35.427494098Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "invalid_request",
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid_request"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          },
          "410": {
            "description": "The kind is retired (`config_kind_retired`): routing, which the router never read, is configured in the global routing rules. The body names `kind` and `successor`; the `Link` header (rel=\"successor-version\") points to it. Versions of a retired kind stay readable.",
            "headers": {
              "Link": {
                "description": "Where the setting lives now: `</v1/routing/versions>; rel=\"successor-version\"`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Configuration kind retired",
                  "status": 410,
                  "code": "config_kind_retired",
                  "detail": "The routing configuration kind is retired; its versions stay readable. Configure it at /v1/routing/versions.",
                  "kind": "routing",
                  "successor": "/v1/routing/versions",
                  "error": {
                    "code": "config_kind_retired",
                    "message": "The routing configuration kind is retired; its versions stay readable. Configure it at /v1/routing/versions."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/contract": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "signPSPContract",
        "summary": "Record the signed contract",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "reason"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The signed copy, up to 10 MB; stored encrypted with its SHA-256."
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "description": "Records the signed contract for the active commercial terms (contract_ref comes from them) and emits psp.contract.signed. It satisfies the contract_signed pilot gate. Requires approved commercial terms.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPpmM4QtZQAtFMAzZOdpsmCab",
                  "account_id": "3KWPpklQ3UMKtjqz5XSeoWPYWKh",
                  "application_id": "3KWPpo6ixiv7LczATPXP4CQXpS6",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "applied",
                  "risk_tier": "medium",
                  "kyb_status": "pending",
                  "state_entered_at": "2026-10-10T22:15:35.185876129Z",
                  "next_review_at": "2027-10-10T22:15:35.218295061Z",
                  "contract_ref": "KP-2026-014",
                  "contract_signed": true,
                  "created_at": "2026-10-10T22:15:35.185876129Z",
                  "updated_at": "2026-10-10T22:15:35.528536855Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "invalid_request",
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid_request"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/technical": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getTechnicalSetup",
        "summary": "Sandbox technical setup",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "The sandbox (test key, mock connectors per method), live-key status and each setup step with its last validation: capability matrix, orientation profile, endpoints (TLS 1.2+ reachability), credentials (certificate validity and key, PGP key fingerprint), webhooks, IP allow-list and statement profile. The PSP completes the steps on the onboarding portal. Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The setup.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TechnicalSetup"
                },
                "example": {
                  "psp_id": "3KWPnnBS0h6YDMR6gHeiAUNjVFo",
                  "status": "technical_setup",
                  "sandbox_enabled": true,
                  "sandbox_key_id": "3KWPnhHlWvZRFKddEUPjtBP6SFc",
                  "sandbox_key_revealable": false,
                  "live_keys_allowed": false,
                  "live_key_revealable": false,
                  "sandbox_methods": [
                    "card"
                  ],
                  "steps": [
                    {
                      "step": "capability_matrix",
                      "status": "passed",
                      "checks": [
                        {
                          "name": "SALE",
                          "result": "pass",
                          "detail": "card; inbound, async, MZN"
                        }
                      ],
                      "config_kind": "capability_matrix",
                      "config_version": 1,
                      "checked_at": "2026-10-10T22:15:20.116851131Z",
                      "value": {
                        "types": [
                          {
                            "type": "SALE",
                            "methods": [
                              "card"
                            ],
                            "direction": "inbound",
                            "mode": "async",
                            "currencies": [
                              "MZN"
                            ],
                            "countries": [
                              "MZ"
                            ],
                            "partial": false,
                            "three_ds": false,
                            "idempotency": false,
                            "confirmation": "callback",
                            "timeout_ms": 30000
                          }
                        ]
                      }
                    },
                    {
                      "step": "orientation_profile",
                      "status": "pending",
                      "checks": [],
                      "config_kind": "orientation_profile"
                    }
                  ],
                  "keys": [
                    {
                      "id": "3KWPnhHlWvZRFKddEUPjtBP6SFc",
                      "account_id": "3KWPnlM19nPUwG6nNuynNezp1w7",
                      "mode": "test",
                      "name": "sandbox",
                      "last4": "uQdZ",
                      "type": "sk",
                      "format": "kp1",
                      "scopes": [
                        "payments:read",
                        "payments:write"
                      ],
                      "created_at": "2026-10-10T22:15:19.893892266Z",
                      "use_count": 0
                    }
                  ],
                  "require_mtls": false,
                  "complete": false
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/sandbox": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "enableSandbox",
        "summary": "Open the sandbox",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Opens the sandbox if that failed on entering technical_setup (psp.sandbox.enable_failed): test-mode mock bindings for every method and a test key the PSP reveals once on the portal. Idempotent. Records psp.sandbox.enabled. Requires `accounts:write`.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPnnBS0h6YDMR6gHeiAUNjVFo",
                  "account_id": "3KWPnlM19nPUwG6nNuynNezp1w7",
                  "application_id": "3KWPnhekPhfRi1KsQ8oQk3l33ma",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "technical_setup",
                  "kyb_status": "in_review",
                  "state_entered_at": "2026-10-10T22:15:19.8755279Z",
                  "contract_signed": false,
                  "sandbox_enabled_at": "2026-10-10T22:15:19.916518232Z",
                  "sandbox_key_id": "3KWPnhHlWvZRFKddEUPjtBP6SFc",
                  "created_at": "2026-10-10T22:15:19.816714735Z",
                  "updated_at": "2026-10-10T22:15:19.947987543Z"
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not in technical_setup or later (`sandbox_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "sandbox_unavailable",
                  "detail": "Not in technical_setup or later (sandbox_unavailable).",
                  "error": {
                    "code": "sandbox_unavailable",
                    "message": "Not in technical_setup or later (sandbox_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/institution": {
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "putPSPInstitution",
        "summary": "Set a PSP's bank code and wallet prefixes",
        "description": "The PSP's place in the payment system (IO-4.2): the 4 digits its NIBs start with and its mobile wallets' MSISDN prefixes (international digits). KulPay infers by them the institution of a biller's credit account (to_psp) and of a deposit's bank (from_psp); no other PSP may hold the same code or an overlapping prefix. Records `psp.institution_updated`. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "bank_code": {
                    "type": "string",
                    "pattern": "^\\d{4}$"
                  },
                  "wallet_prefixes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "reason": {
                    "type": "string"
                  },
                  "wallet_method": {
                    "type": "string",
                    "example": "mpesa",
                    "description": "The payout method its wallets are paid by: remittances to a biller's wallet go through it (IO-4.3)."
                  }
                }
              },
              "example": {
                "bank_code": "0008",
                "wallet_prefixes": [],
                "reason": "BCI"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPnnBS0h6YDMR6gHeiAUNjVFo",
                  "account_id": "3KWPnlM19nPUwG6nNuynNezp1w7",
                  "application_id": "3KWPnhekPhfRi1KsQ8oQk3l33ma",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "technical_setup",
                  "kyb_status": "in_review",
                  "state_entered_at": "2026-10-10T22:15:19.8755279Z",
                  "contract_signed": false,
                  "sandbox_enabled_at": "2026-10-10T22:15:19.916518232Z",
                  "sandbox_key_id": "3KWPnhHlWvZRFKddEUPjtBP6SFc",
                  "created_at": "2026-10-10T22:15:19.816714735Z",
                  "updated_at": "2026-10-10T22:15:19.947987543Z"
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not in technical_setup or later (`sandbox_unavailable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "sandbox_unavailable",
                  "detail": "Not in technical_setup or later (sandbox_unavailable).",
                  "error": {
                    "code": "sandbox_unavailable",
                    "message": "Not in technical_setup or later (sandbox_unavailable)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/commissions": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getCommissionStatement",
        "summary": "A PSP's commissions as a participant",
        "description": "What KulPay owes the PSP for its part in others' payments in a month (IO-4.4): each succeeded bill payment it took part in (initiating_psp, from_psp, to_psp) whose role a rule of its fee schedule prices (rule `role`), the amount posted to its balance with the payment as the reference, and the totals per currency. `month` is YYYY-MM (this month by default). Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The statement.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "month": {
                      "type": "string",
                      "example": "2026-10"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "example": {
                        "MZN": "1250.00"
                      }
                    },
                    "commissions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "payment_kind": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string",
                            "enum": [
                              "initiating_psp",
                              "from_psp",
                              "to_psp"
                            ]
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "base_minor": {
                            "type": "integer"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "schedule_id": {
                            "type": "string"
                          },
                          "schedule_version": {
                            "type": "integer"
                          },
                          "rule": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "psp_id": "3KXgPSPkulOffice00000000001",
                  "month": "2026-10",
                  "mode": "live",
                  "totals": {
                    "MZN": "12.50"
                  },
                  "commissions": [
                    {
                      "id": "3KXgCOM00000000000000000001",
                      "payment_id": "3KXgBILL0000000000000000001",
                      "payment_kind": "bill_payment",
                      "role": "initiating_psp",
                      "psp_id": "3KXgPSPkulOffice00000000001",
                      "account_id": "3KXgACCkulOffice0000000001",
                      "mode": "live",
                      "currency": "MZN",
                      "base_minor": 125000,
                      "amount_minor": 1250,
                      "schedule_id": "3KXgFEE00000000000000000001",
                      "schedule_version": 2,
                      "rule": 0,
                      "created_at": "2026-10-15T10:00:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/credit-report": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getCreditReport",
        "summary": "A resolver's credits of a day",
        "description": "A resolver's credits applied on a day (IO-6.2), yesterday by default: deposit, credit, amount, status, when applied, the resolver's transaction and the settlement batch that paid it, with the totals per currency. `?format=csv` answers CSV. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resolver_psp_id": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "credits": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "deposit_id": {
                            "type": "string"
                          },
                          "credit_id": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "applied_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "transaction_id": {
                            "type": "string"
                          },
                          "settlement_batch_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "resolver_psp_id": "3KXgPSPkulOffice00000000001",
                  "date": "2026-10-14",
                  "mode": "live",
                  "totals": {
                    "MZN": "500.00"
                  },
                  "credits": [
                    {
                      "deposit_id": "3KXgDEP00000000000000000001",
                      "credit_id": "3KXgCRD00000000000000000001",
                      "amount": "500.00",
                      "currency": "MZN",
                      "status": "applied",
                      "applied_at": "2026-10-14T09:12:00Z",
                      "transaction_id": "txn_blnk_1",
                      "settlement_batch_id": "3KXgBAT00000000000000000001"
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/credit-reconciliations": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listCreditReconciliations",
        "summary": "The days of credits reconciled",
        "description": "The days resolvers reconciled (IO-6.2), of one resolver (`psp_id`) or all, of a `date` or all, newest first: KulPay's applied total, the resolver's ledger and settled totals, what is missing on each side, and whether the day closed. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "psp_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The days.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "resolver_psp_id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "applied_minor": {
                            "type": "integer"
                          },
                          "ledger_minor": {
                            "type": "integer"
                          },
                          "settled_minor": {
                            "type": "integer"
                          },
                          "missing_at_resolver": {
                            "type": "integer"
                          },
                          "missing_at_kulpay": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "closed",
                              "differences"
                            ]
                          },
                          "reported_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KXgREC00000000000000000001",
                      "resolver_psp_id": "3KXgPSPkulOffice00000000001",
                      "date": "2026-10-14",
                      "mode": "live",
                      "currency": "MZN",
                      "applied_minor": 50000,
                      "ledger_minor": 50000,
                      "settled_minor": 50000,
                      "missing_at_resolver": 0,
                      "missing_at_kulpay": 0,
                      "status": "closed",
                      "reported_at": "2026-10-15T02:00:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/certification/runs": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "startCertification",
        "summary": "Start a certification run",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Runs the suite generated from the approved capability matrix in the PSP's sandbox, in the background: for each payment method every outcome (success, each decline family, timeout, unknown resolved by status query, duplicate), refund, capture, void and payout flows; for a matrix that declares BILL_PAYMENT, a bill sold through the sandbox reference biller (REF_SANDBOX, from `kulpay-payments sandbox seed`) and one the biller refuses, refunded to the payer; for CASH_IN, a deposit initiated against a sandbox reference and confirmed, and one of the wrong amount refused; then idempotency replay, webhook delivery, redelivery and a badly signed webhook to the PSP's endpoint, and the sample statement, parsed with the parser and options of the PSP's statement profile (camt.053, camt.054, MT940, BAI2, CSV, \u2026), matched against the run's payments. With `rerun_of`, only that run's failed cases run again; the passed ones are carried over. When every mandatory case passes, a report is signed (audit signing key) and a PSP in technical_setup moves to certification. Records psp.certification.run_started, case_failed, passed or failed. Requires `accounts:write`.",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "rerun_of": {
                    "type": "string"
                  },
                  "statement": {
                    "type": "string",
                    "format": "binary",
                    "description": "Sample statement, CSV with reference, amount, currency and date columns (at most 1 MB)."
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rerun_of": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "rerun_of": "string"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The run, started.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificationRun"
                },
                "example": {
                  "id": "3KWPpcnth1lP0kp4qrkzjWdkBn9",
                  "psp_id": "3KWPpNpKmnm7b4atBTqlciEy2Ky",
                  "status": "running",
                  "rerun_of": "3KWPpMHNDSF9IxULJpbdY6NaH6F",
                  "cases": 13,
                  "mandatory": 13,
                  "passed": 0,
                  "failed": 0,
                  "capability_matrix_version": 1,
                  "started_by": "key_accounts",
                  "started_at": "2026-10-10T22:15:34.655950849Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: no approved capability matrix, sandbox not open.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "invalid_request: no approved capability matrix, sandbox not open.",
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid_request: no approved capability matrix, sandbox not open."
                  }
                }
              }
            }
          },
          "409": {
            "description": "A run is in progress (`certification_running`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "certification_running",
                  "detail": "A run is in progress (certification_running).",
                  "error": {
                    "code": "certification_running",
                    "message": "A run is in progress (certification_running)."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listCertifications",
        "summary": "Certification runs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Runs, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CertificationRun"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPpMHNDSF9IxULJpbdY6NaH6F",
                      "psp_id": "3KWPpNpKmnm7b4atBTqlciEy2Ky",
                      "status": "running",
                      "cases": 13,
                      "mandatory": 13,
                      "passed": 0,
                      "failed": 0,
                      "capability_matrix_version": 1,
                      "started_by": "psp:3KWPpNpKmnm7b4atBTqlciEy2Ky",
                      "started_at": "2026-10-10T22:15:32.545774684Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/certification/runs/{run}": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getCertification",
        "summary": "A run with its cases",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "run",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/CertificationRun"
                    },
                    "cases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CertificationCase"
                      }
                    }
                  }
                },
                "example": {
                  "run": {
                    "id": "3KWPpcnth1lP0kp4qrkzjWdkBn9",
                    "psp_id": "3KWPpNpKmnm7b4atBTqlciEy2Ky",
                    "status": "passed",
                    "rerun_of": "3KWPpMHNDSF9IxULJpbdY6NaH6F",
                    "cases": 13,
                    "mandatory": 13,
                    "passed": 13,
                    "failed": 0,
                    "capability_matrix_version": 1,
                    "started_by": "key_accounts",
                    "started_at": "2026-10-10T22:15:34.655950849Z",
                    "finished_at": "2026-10-10T22:15:34.719113129Z",
                    "report_sha256": "4ffffee44c214941549f25423c6505b12c9e18e4a51bde81a6d56f0a7b88c526",
                    "signature": "UCOtNjozn1QyAPH7R7nR0IQWN+phEJF1930iZtdDq71lmyZtw8Ypik3W27T5t27SkJQ1SGDG2alsO5bsyVMXAQ==",
                    "signing_key_id": "cert-http"
                  },
                  "cases": [
                    {
                      "id": "3KWPpb41DZtVM0Ha86ZeN0kgNg5",
                      "run_id": "3KWPpcnth1lP0kp4qrkzjWdkBn9",
                      "seq": 1,
                      "key": "payment/card/success",
                      "kind": "outcome",
                      "title": "card payment succeeds",
                      "mandatory": true,
                      "status": "passed",
                      "expected": "status succeeded",
                      "actual": "status succeeded",
                      "payment_id": "3KWPpOxjsoZcAvZYdKnt3fVUcGX",
                      "carried_from": "3KWPpMHNDSF9IxULJpbdY6NaH6F"
                    },
                    {
                      "id": "3KWPpaUUNF9C0XaZCAGg8Y2gsFv",
                      "run_id": "3KWPpcnth1lP0kp4qrkzjWdkBn9",
                      "seq": 2,
                      "key": "payment/card/decline/card_error",
                      "kind": "outcome",
                      "title": "card decline: card_error",
                      "mandatory": true,
                      "status": "passed",
                      "expected": "status failed, failure_code card_declined",
                      "actual": "status failed, failure_code card_declined",
                      "payment_id": "3KWPpLpbGFUi7C15n4YcBkGn1AG",
                      "carried_from": "3KWPpMHNDSF9IxULJpbdY6NaH6F"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`not_found`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found",
                  "error": {
                    "code": "not_found",
                    "message": "not_found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/certification/runs/{run}/events": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "streamCertification",
        "summary": "Follow a run live",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "run",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Server-sent events: `case` ({key, status, actual, payment_id}) whenever a case changes, then `run` ({status, passed, failed}) when it finishes.",
        "responses": {
          "200": {
            "description": "An event stream.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/certification/runs/{run}/report.json": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "certificationReportJSON",
        "summary": "Signed report (JSON)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "run",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "{report, sha256, signature, signing_key_id, public_key, algorithm}: verify the Ed25519 signature over the exact bytes of `report`.",
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "algorithm": "Ed25519 over the report bytes",
                  "public_key": "/RckOFqgx1tk+3jNYC+h2ZH96/drE8WO1wLqyDXp9hg=",
                  "report": {
                    "format": "kulpay.psp_certification.v1",
                    "result": "passed",
                    "psp": {
                      "account_id": "3KWPpP8Jvi1WplT1iduLQKZYpeF",
                      "id": "3KWPpNpKmnm7b4atBTqlciEy2Ky",
                      "legal_name": "Banco Exemplo SA",
                      "region": "mz"
                    },
                    "run": {
                      "duration_seconds": 0,
                      "finished_at": "2026-10-10T22:15:34Z",
                      "id": "3KWPpcnth1lP0kp4qrkzjWdkBn9",
                      "rerun_of": "3KWPpMHNDSF9IxULJpbdY6NaH6F",
                      "started_at": "2026-10-10T22:15:34Z"
                    },
                    "capability_matrix_version": 1,
                    "mandatory_cases": 13,
                    "passed": 13,
                    "failed": 0,
                    "cases": [
                      {
                        "key": "payment/card/success",
                        "title": "card payment succeeds",
                        "mandatory": true,
                        "status": "passed",
                        "expected": "status succeeded",
                        "actual": "status succeeded",
                        "payment_id": "3KWPpOxjsoZcAvZYdKnt3fVUcGX",
                        "carried_from": "3KWPpMHNDSF9IxULJpbdY6NaH6F"
                      },
                      {
                        "key": "payment/card/decline/card_error",
                        "title": "card decline: card_error",
                        "mandatory": true,
                        "status": "passed",
                        "expected": "status failed, failure_code card_declined",
                        "actual": "status failed, failure_code card_declined",
                        "payment_id": "3KWPpLpbGFUi7C15n4YcBkGn1AG",
                        "carried_from": "3KWPpMHNDSF9IxULJpbdY6NaH6F"
                      }
                    ]
                  },
                  "sha256": "4ffffee44c214941549f25423c6505b12c9e18e4a51bde81a6d56f0a7b88c526",
                  "signature": "UCOtNjozn1QyAPH7R7nR0IQWN+phEJF1930iZtdDq71lmyZtw8Ypik3W27T5t27SkJQ1SGDG2alsO5bsyVMXAQ==",
                  "signing_key_id": "cert-http"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` (not finished).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found (not finished).",
                  "error": {
                    "code": "not_found",
                    "message": "not_found (not finished)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/certification/runs/{run}/report.pdf": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "certificationReportPDF",
        "summary": "Report (PDF)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "run",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report, with its SHA-256 and signature.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/go-live": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getGoLive",
        "summary": "Pilot and go-live state",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Pilot gates (KYB approved, contract signed, commercial terms approved, certification passed), live gates (7 days in pilot since entry or the last critical incident resolved, no open critical incident, for a PSP, live statements since pilot all matched; for a biller, a live remittance paid during the pilot, or clean supplier reconciliations when it sells only principal-model products), pilot settings, live key and incidents.",
        "responses": {
          "200": {
            "description": "The state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GoLive"
                },
                "example": {
                  "status": "pilot",
                  "pilot_gates": [
                    {
                      "name": "kyb_approved",
                      "met": true
                    },
                    {
                      "name": "contract_signed",
                      "met": true
                    }
                  ],
                  "live_gates": [
                    {
                      "name": "pilot_7_days",
                      "met": false,
                      "detail": "0 of 7 days since 2026-10-10 22:15"
                    },
                    {
                      "name": "no_critical_incident",
                      "met": true,
                      "detail": "0 open critical incident(s)"
                    }
                  ],
                  "pilot_settings": {
                    "per_transaction": "40000",
                    "daily": "100000",
                    "monthly": "2000000",
                    "currency": "MZN",
                    "merchants": [],
                    "routing_weight": 10
                  },
                  "live_key_id": "3KWPpr5y02e3qlgVBBep5PQmDrF",
                  "incidents": [],
                  "go_live_checklist_stored": false
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "requestGoLive",
        "summary": "Prepare go-live (maker)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "7 clean days"
              }
            }
          }
        },
        "description": "From pilot, with every live gate met, prepares a psp.go_live approval that a different person decides (POST /v1/admin/approvals/{id}/decide). Approval stores the go-live checklist, with the signed certification report, and moves the PSP to live. Moving to live any other way is refused.",
        "responses": {
          "202": {
            "description": "Prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPpq3PJfN5Rmqed0WaMbdtOKc",
                    "kind": "psp.go_live",
                    "subject_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                    "tenant_id": "3KWPpsYfFNrQEhm84v3anWxHrHj",
                    "reason": "7 clean days",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-18T22:15:36.187945473Z"
                  },
                  "message": "go-live prepared; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "400": {
            "description": "Gates not met (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "live gates not met: pilot_7_days, no_critical_incident, reconciliation_matched",
                  "error": {
                    "code": "invalid_request",
                    "message": "live gates not met: pilot_7_days, no_critical_incident, reconciliation_matched"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/go-live/checklist": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getGoLiveChecklist",
        "summary": "Go-live checklist",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "What was true when go-live was approved: approvers, pilot and live gates, KYB, contract, commercial terms version, the signed certification report, pilot settings and incidents.",
        "responses": {
          "200": {
            "description": "The checklist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "approval": {
                    "approved_by": "key_checker",
                    "id": "3KWPpq3PJfN5Rmqed0WaMbdtOKc",
                    "prepared_by": "key_accounts",
                    "reason": "7 clean days"
                  },
                  "certification": {
                    "run_id": ""
                  },
                  "commercial_terms_version": 1,
                  "contract": {
                    "ref": "",
                    "signed": true
                  },
                  "format": "kulpay.psp_go_live_checklist.v1",
                  "generated_at": "2026-10-18T22:15:36Z",
                  "incidents": [
                    {
                      "id": "3KWPpuIL9mUgnP4edWkQbxtFzg9",
                      "psp_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                      "severity": "critical",
                      "summary": "callbacks stalled",
                      "opened_by": "key_accounts",
                      "opened_at": "2026-10-10T22:15:36.187945473Z",
                      "resolution": "fixed",
                      "resolved_by": "key_accounts",
                      "resolved_at": "2026-10-10T22:15:36.187945473Z"
                    }
                  ],
                  "kyb": {
                    "approved_at": null,
                    "risk_tier": "medium",
                    "status": "approved"
                  },
                  "live_gates": [
                    {
                      "name": "pilot_7_days",
                      "met": true,
                      "detail": "8 of 7 days since 2026-10-10 22:15"
                    },
                    {
                      "name": "no_critical_incident",
                      "met": true,
                      "detail": "0 open critical incident(s)"
                    }
                  ],
                  "pilot": {
                    "entered_at": "2026-10-10T22:15:36.187945473Z",
                    "settings": {
                      "per_transaction": "40000",
                      "daily": "100000",
                      "monthly": "2000000",
                      "currency": "MZN",
                      "merchants": [
                        "m-1"
                      ],
                      "routing_weight": 20
                    },
                    "started_at": "2026-10-10T22:15:36.187945473Z"
                  },
                  "pilot_gates": [
                    {
                      "name": "kyb_approved",
                      "met": true
                    },
                    {
                      "name": "contract_signed",
                      "met": true
                    }
                  ],
                  "psp": {
                    "account_id": "3KWPpsYfFNrQEhm84v3anWxHrHj",
                    "id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                    "legal_name": "Banco Exemplo SA",
                    "region": "mz"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`: not live through an approval.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not_found: not live through an approval.",
                  "error": {
                    "code": "not_found",
                    "message": "not_found: not live through an approval."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/pilot": {
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "setPilotSettings",
        "summary": "Pilot limits, merchants and routing weight",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/PilotSettings"
                  },
                  {
                    "type": "object",
                    "required": [
                      "reason"
                    ],
                    "properties": {
                      "reason": {
                        "type": "string"
                      }
                    }
                  }
                ]
              },
              "example": {
                "per_transaction": "40000",
                "daily": "100000",
                "monthly": "2000000",
                "merchants": [
                  "m-1"
                ],
                "routing_weight": 20,
                "reason": "first merchant"
              }
            }
          }
        },
        "description": "In pilot only. Entering pilot sets 10% of the contracted limits, no merchants and a 10% weight. Live payments beyond the limits, or without an allowed metadata.merchant_id, are refused (422 gateway.pilot_limit).",
        "responses": {
          "200": {
            "description": "The settings.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PilotSettings"
                },
                "example": {
                  "per_transaction": "40000",
                  "daily": "100000",
                  "monthly": "2000000",
                  "currency": "MZN",
                  "merchants": [
                    "m-1"
                  ],
                  "routing_weight": 20
                }
              }
            }
          },
          "400": {
            "description": "Above the contract, or not in pilot (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "per_transaction 500000 exceeds the contracted 400000",
                  "error": {
                    "code": "invalid_request",
                    "message": "per_transaction 500000 exceeds the contracted 400000"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/pilot/setup": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "setupPilot",
        "summary": "Retry the pilot setup",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "If psp.pilot.setup_failed was recorded on entering pilot: sets the default pilot settings and issues the live key. Idempotent.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "legal_name": "Electricidade de Mo\u00e7ambique",
                  "registration_number": "string",
                  "country": "MZ",
                  "region": "string",
                  "language": "pt",
                  "status": "applied",
                  "kyb_status": "pending",
                  "resume_state": "succeeded",
                  "risk_tier": "string",
                  "state_entered_at": "2026-10-10T09:30:00Z",
                  "kyb_approved_at": "2026-10-10T09:30:00Z",
                  "live_at": "2026-10-10T09:30:00Z",
                  "contract_ref": "string",
                  "contract_signed": true,
                  "sandbox_enabled_at": "2026-10-10T09:30:00Z",
                  "sandbox_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "certified_at": "2026-10-10T09:30:00Z",
                  "certification_run_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "pilot_started_at": "2026-10-10T09:30:00Z",
                  "live_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "last_review_at": "2026-10-10T09:30:00Z",
                  "review_notice": "",
                  "emergency_suspended_at": "2026-10-10T09:30:00Z",
                  "suspension_review_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "suspension_review_overdue": true
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/incidents": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "recordIncident",
        "summary": "Record an incident",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "severity",
                  "summary"
                ],
                "properties": {
                  "severity": {
                    "enum": [
                      "critical",
                      "major",
                      "minor"
                    ]
                  },
                  "summary": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "severity": "critical",
                "summary": "callbacks stalled"
              }
            }
          }
        },
        "description": "A critical incident holds go-live; the 7 pilot days count again from its resolution.",
        "responses": {
          "201": {
            "description": "The incident.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPIncident"
                },
                "example": {
                  "id": "3KWPpuIL9mUgnP4edWkQbxtFzg9",
                  "psp_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                  "severity": "critical",
                  "summary": "callbacks stalled",
                  "opened_by": "key_accounts",
                  "opened_at": "2026-10-10T22:15:36.187945473Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/incidents/{incident}/resolve": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "resolveIncident",
        "summary": "Resolve an incident",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "incident",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "resolution"
                ],
                "properties": {
                  "resolution": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "resolution": "fixed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The incident.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSPIncident"
                },
                "example": {
                  "id": "3KWPpuIL9mUgnP4edWkQbxtFzg9",
                  "psp_id": "3KWPpp00pI0ILGNVqqM3kUpSxNv",
                  "severity": "critical",
                  "summary": "callbacks stalled",
                  "opened_by": "key_accounts",
                  "opened_at": "2026-10-10T22:15:36.187945473Z",
                  "resolution": "fixed",
                  "resolved_by": "key_accounts",
                  "resolved_at": "2026-10-10T22:15:36.187945473Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psp-reviews": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listPSPReviews",
        "summary": "Review queue",
        "description": "Periodic reviews due within 30 days or overdue (high risk every 6 months, medium 12, low 24) and emergency suspensions waiting for a second officer (24 hours). An hourly job records psp.review.due and psp.suspension.review_overdue.",
        "responses": {
          "200": {
            "description": "The queue, most urgent first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReviewItem"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "psp": {
                        "id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                        "account_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                        "application_id": "3KWPq6zQflNIw3ra10OHzdZABgk",
                        "kind": "psp",
                        "legal_name": "Banco Exemplo SA",
                        "registration_number": "100234567",
                        "country": "MZ",
                        "region": "",
                        "language": "pt",
                        "status": "suspended",
                        "resume_state": "technical_setup",
                        "risk_tier": "high",
                        "kyb_status": "in_review",
                        "state_entered_at": "2026-10-10T22:15:38.709280961Z",
                        "next_review_at": "2027-04-10T22:15:38.627684253Z",
                        "contract_signed": false,
                        "sandbox_enabled_at": "2026-10-10T22:15:38.605512701Z",
                        "sandbox_key_id": "3KWPqBPUHHtpYIo3jN74t1OHRVj",
                        "emergency_suspended_at": "2026-10-10T22:15:38.710738721Z",
                        "suspension_review_id": "3KWPqBtjd4R5ETH8OB7th9QWOjF",
                        "created_at": "2026-10-10T22:15:38.521919662Z",
                        "updated_at": "2026-10-10T22:15:38.709280961Z"
                      },
                      "kind": "emergency_suspension",
                      "due_at": "2026-10-11T22:15:38.710738721Z",
                      "overdue": false,
                      "approval_id": "3KWPqBtjd4R5ETH8OB7th9QWOjF"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/reviews": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "completePSPReview",
        "summary": "Complete a periodic review",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcome",
                  "notes"
                ],
                "properties": {
                  "outcome": {
                    "enum": [
                      "satisfactory",
                      "actions_required",
                      "escalated"
                    ]
                  },
                  "notes": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "outcome": "satisfactory",
                "notes": "licence current"
              }
            }
          }
        },
        "description": "Records psp.review.completed and schedules the next review by risk tier.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                  "account_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                  "application_id": "3KWPq6zQflNIw3ra10OHzdZABgk",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "suspended",
                  "resume_state": "technical_setup",
                  "risk_tier": "high",
                  "kyb_status": "in_review",
                  "state_entered_at": "2026-10-10T22:15:38.822160182Z",
                  "next_review_at": "2027-04-10T22:15:38.858035949Z",
                  "contract_signed": false,
                  "sandbox_enabled_at": "2026-10-10T22:15:38.605512701Z",
                  "sandbox_key_id": "3KWPqBPUHHtpYIo3jN74t1OHRVj",
                  "last_review_at": "2026-10-10T22:15:38.858035949Z",
                  "created_at": "2026-10-10T22:15:38.521919662Z",
                  "updated_at": "2026-10-10T22:15:38.858035949Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/suspension": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "suspendPSP",
        "summary": "Suspend a PSP",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  },
                  "emergency": {
                    "type": "boolean",
                    "description": "Suspend now on one officer's word; a second reviews within 24 hours."
                  }
                }
              },
              "example": {
                "emergency": true,
                "reason": "card testing"
              }
            }
          }
        },
        "description": "Suspension blocks the PSP's account (no new payments, checkouts or links) and refuses its keys from the next request on every node; payments in flight run to their end. Normally prepared by one person and approved by another (202). With emergency, it takes effect at once (200) and creates a psp.suspension.review approval: upholding it keeps the suspension, rejecting it reinstates the PSP.",
        "responses": {
          "200": {
            "description": "Suspended (emergency).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp": {
                      "$ref": "#/components/schemas/PSP"
                    },
                    "review": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "message": "suspended now; a second officer must review it within 24 hours",
                  "psp": {
                    "id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                    "account_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                    "application_id": "3KWPq6zQflNIw3ra10OHzdZABgk",
                    "kind": "psp",
                    "legal_name": "Banco Exemplo SA",
                    "registration_number": "100234567",
                    "country": "MZ",
                    "region": "",
                    "language": "pt",
                    "status": "suspended",
                    "resume_state": "technical_setup",
                    "risk_tier": "high",
                    "kyb_status": "in_review",
                    "state_entered_at": "2026-10-10T22:15:38.709280961Z",
                    "next_review_at": "2027-04-10T22:15:38.627684253Z",
                    "contract_signed": false,
                    "sandbox_enabled_at": "2026-10-10T22:15:38.605512701Z",
                    "sandbox_key_id": "3KWPqBPUHHtpYIo3jN74t1OHRVj",
                    "emergency_suspended_at": "2026-10-10T22:15:38.710738721Z",
                    "suspension_review_id": "3KWPqBtjd4R5ETH8OB7th9QWOjF",
                    "created_at": "2026-10-10T22:15:38.521919662Z",
                    "updated_at": "2026-10-10T22:15:38.709280961Z"
                  },
                  "review": {
                    "id": "3KWPqBtjd4R5ETH8OB7th9QWOjF",
                    "kind": "psp.suspension.review",
                    "subject_id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                    "tenant_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                    "reason": "review the emergency suspension: card testing",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:38.707281265Z"
                  }
                }
              }
            }
          },
          "202": {
            "description": "Prepared; a second person decides it (POST /v1/admin/approvals/{id}/decide).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPq7pLTTBVFL4s8IbSgriXzE8",
                    "kind": "psp.suspend",
                    "subject_id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                    "tenant_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                    "reason": "SLA breaches",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:38.802980169Z"
                  },
                  "message": "suspension prepared; a second person must approve it"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "invalid_request",
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid_request"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/reinstate": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "reinstatePSP",
        "summary": "Reinstate a suspended PSP",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "description": "Back to the state it left, with its account status and keys restored. Records psp.reinstated.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "legal_name": "Electricidade de Mo\u00e7ambique",
                  "registration_number": "string",
                  "country": "MZ",
                  "region": "string",
                  "language": "pt",
                  "status": "applied",
                  "kyb_status": "pending",
                  "resume_state": "succeeded",
                  "risk_tier": "string",
                  "state_entered_at": "2026-10-10T09:30:00Z",
                  "kyb_approved_at": "2026-10-10T09:30:00Z",
                  "live_at": "2026-10-10T09:30:00Z",
                  "contract_ref": "string",
                  "contract_signed": true,
                  "sandbox_enabled_at": "2026-10-10T09:30:00Z",
                  "sandbox_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "certified_at": "2026-10-10T09:30:00Z",
                  "certification_run_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "pilot_started_at": "2026-10-10T09:30:00Z",
                  "live_key_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "last_review_at": "2026-10-10T09:30:00Z",
                  "review_notice": "",
                  "emergency_suspended_at": "2026-10-10T09:30:00Z",
                  "suspension_review_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "suspension_review_overdue": true
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/offboarding": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getOffboarding",
        "summary": "What offboarded waits for",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Gates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "gates": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Gate"
                      }
                    }
                  }
                },
                "example": {
                  "gates": [
                    {
                      "name": "routing_drained",
                      "met": true,
                      "detail": "the account takes no new payments"
                    },
                    {
                      "name": "credentials_revoked",
                      "met": true,
                      "detail": "0 active key(s)"
                    }
                  ],
                  "status": "offboarding"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "offboardPSP",
        "summary": "Prepare offboarding (maker)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "contract ended"
              }
            }
          }
        },
        "description": "From pilot, live or suspended. On approval by a second person: no new payments, every key revoked, psp.offboarding.started.",
        "responses": {
          "202": {
            "description": "Prepared; a second person decides it (POST /v1/admin/approvals/{id}/decide).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPq6oHbsrNPhdipq2aUDNz3FJ",
                    "kind": "psp.offboard",
                    "subject_id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                    "tenant_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                    "reason": "contract ended",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:38.88584572Z"
                  },
                  "message": "offboarding prepared; a second person must approve it"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "invalid_request",
                  "error": {
                    "code": "invalid_request",
                    "message": "invalid_request"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/offboarding/complete": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "completeOffboarding",
        "summary": "Complete offboarding",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "settled"
              }
            }
          }
        },
        "description": "Once routing is drained, credentials revoked, live payments final and reconciliation closed: offboarded (psp.offboarded). Nothing is deleted.",
        "responses": {
          "200": {
            "description": "The PSP.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PSP"
                },
                "example": {
                  "id": "3KWPq84JWIgPA0bRlrInvqXVHZS",
                  "account_id": "3KWPqAm6bUbJrAb7hW8f2ZU6pPT",
                  "application_id": "3KWPq6zQflNIw3ra10OHzdZABgk",
                  "kind": "psp",
                  "legal_name": "Banco Exemplo SA",
                  "registration_number": "100234567",
                  "country": "MZ",
                  "region": "",
                  "language": "pt",
                  "status": "offboarded",
                  "risk_tier": "high",
                  "kyb_status": "in_review",
                  "state_entered_at": "2026-10-10T22:15:38.950522963Z",
                  "next_review_at": "2027-04-10T22:15:38.858035949Z",
                  "contract_signed": false,
                  "sandbox_enabled_at": "2026-10-10T22:15:38.605512701Z",
                  "sandbox_key_id": "3KWPqBPUHHtpYIo3jN74t1OHRVj",
                  "last_review_at": "2026-10-10T22:15:38.858035949Z",
                  "created_at": "2026-10-10T22:15:38.521919662Z",
                  "updated_at": "2026-10-10T22:15:38.950522963Z"
                }
              }
            }
          },
          "400": {
            "description": "Open items remain (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "Open items remain (invalid_request).",
                  "error": {
                    "code": "invalid_request",
                    "message": "Open items remain (invalid_request)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/transaction-types": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "listTransactionTypes",
        "summary": "Transaction type catalogue",
        "security": [],
        "description": "Every transaction type with its money flow (debit and credit sides) and parent rules, and the currencies with their minor-unit exponents. Public reference data. Lists the supported types; `?include=reserved` adds the names kept for later (TRANSFER, MANDATE, RECURRING, TOKENIZE, BALANCE_INQUIRY, FX_QUOTE), which KulPay does not process yet and no capability matrix may declare.",
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TransactionType"
                      }
                    },
                    "currencies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Currency"
                      }
                    }
                  }
                },
                "example": {
                  "currencies": [
                    {
                      "code": "AED",
                      "exponent": 2,
                      "name": "UAE dirham"
                    },
                    {
                      "code": "AOA",
                      "exponent": 2,
                      "name": "Angolan kwanza"
                    }
                  ],
                  "data": [
                    {
                      "code": "SALE",
                      "name": "Sale",
                      "description": "Authorise and capture in one step: the customer pays the merchant.",
                      "moves_money": true,
                      "holds_funds": false,
                      "debit": "customer",
                      "credit": "merchant",
                      "amount_required": true
                    },
                    {
                      "code": "AUTH",
                      "name": "Authorisation",
                      "description": "Reserve funds on the customer's account without moving them; a capture or void follows.",
                      "moves_money": false,
                      "holds_funds": true,
                      "debit": "customer",
                      "amount_required": true
                    }
                  ]
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "include",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "reserved"
              ]
            },
            "description": "reserved: list the reserved types too."
          }
        ]
      }
    },
    "/v1/psps/{id}/capabilities": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getCapabilities",
        "summary": "Your capability matrix",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "For the PSP the key belongs to (scope `psp:configure`): the active matrix and version, a pending one if any, and the connector capability flags (refunds, partial_refunds, voids, 3ds) generated from it, by method.",
        "responses": {
          "200": {
            "description": "The matrix.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "active": {
                      "$ref": "#/components/schemas/CapabilityMatrix"
                    },
                    "active_version": {
                      "type": "integer"
                    },
                    "pending": {
                      "$ref": "#/components/schemas/CapabilityMatrix"
                    },
                    "pending_version": {
                      "type": "integer"
                    },
                    "connector_flags": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "psp_id": "3KWPlauKgv7rrVi1Wsue46ebEDF",
                  "pending": {
                    "types": [
                      {
                        "type": "SALE",
                        "methods": [
                          "card"
                        ],
                        "direction": "inbound",
                        "mode": "async",
                        "currencies": [
                          "MZN"
                        ],
                        "countries": [
                          "MZ"
                        ],
                        "min_amount": "1",
                        "max_amount": "100000",
                        "partial": false,
                        "three_ds": true,
                        "idempotency": true,
                        "confirmation": "both",
                        "expected_latency_ms": 1500,
                        "timeout_ms": 20000
                      }
                    ]
                  },
                  "pending_version": 1
                }
              }
            }
          },
          "404": {
            "description": "Not your PSP (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "PSP not found",
                  "error": {
                    "code": "not_found",
                    "message": "PSP not found"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "putCapabilities",
        "summary": "Change your capability matrix",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CapabilityMatrix"
                  },
                  {
                    "type": "object",
                    "required": [
                      "reason"
                    ],
                    "properties": {
                      "reason": {
                        "type": "string"
                      }
                    }
                  }
                ]
              },
              "example": {
                "types": [
                  {
                    "type": "SALE",
                    "methods": [
                      "card"
                    ],
                    "currencies": [
                      "MZN"
                    ],
                    "countries": [
                      "MZ"
                    ],
                    "min_amount": "1",
                    "max_amount": "100000",
                    "partial": false,
                    "three_ds": true,
                    "idempotency": true,
                    "confirmation": "both",
                    "expected_latency_ms": 1500,
                    "timeout_ms": 20000
                  }
                ],
                "reason": "first matrix"
              }
            }
          }
        },
        "description": "Creates a pending version (config.capability_matrix.version_created) that KulPay approves. For a PSP in pilot or live, a version that adds types is approved only after a certification run of that version passes; the suite runs on the pending version. Scope `psp:configure`.",
        "responses": {
          "202": {
            "description": "Pending approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "$ref": "#/components/schemas/ConfigVersion"
                    },
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPlejDpniZIMJi2oDCy8F9wj4",
                    "kind": "config.activate",
                    "subject_id": "3KWPlauKgv7rrVi1Wsue46ebEDF",
                    "tenant_id": "3KWPlZbqGHdlsM4XZrsJ7t1bZLx",
                    "payload": "{\"version_id\":\"3KWPlcQzdNHk683phJtjBHuTiUO\",\"kind\":\"capability_matrix\",\"version\":1}",
                    "reason": "capability_matrix v1: first matrix",
                    "prepared_by": "3KWPlf5E3HSh19wzHUJIxMjitjS",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:02.732993386Z"
                  },
                  "message": "capability matrix version prepared; KulPay approves it before it is active",
                  "version": {
                    "id": "3KWPlcQzdNHk683phJtjBHuTiUO",
                    "psp_id": "3KWPlauKgv7rrVi1Wsue46ebEDF",
                    "kind": "capability_matrix",
                    "version": 1,
                    "status": "pending",
                    "reason": "first matrix",
                    "approval_id": "3KWPlejDpniZIMJi2oDCy8F9wj4",
                    "created_by": "3KWPlf5E3HSh19wzHUJIxMjitjS",
                    "created_at": "2026-10-10T22:15:02.730855834Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The matrix breaks a rule (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "capability matrix: CAPTURE: needs one of AUTH enabled",
                  "error": {
                    "code": "invalid_request",
                    "message": "capability matrix: CAPTURE: needs one of AUTH enabled"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not your PSP (`not_found`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "Not your PSP (not_found).",
                  "error": {
                    "code": "not_found",
                    "message": "Not your PSP (not_found)."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/circuits": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "pspCircuits",
        "summary": "A PSP's circuits",
        "description": "Per operation (authorize, payout) on this node: closed, open (routing skips the PSP) or half-open, with the window's requests and failures (PSP-9.4). Defaults: open at 50% failures over at least 20 calls in 30 s, half-open after 30 s with 5 trial calls; a PSP tunes them in its retry_policy `breaker`. Opening records `psp.health.degraded`, closing `psp.health.recovered`. Requires `admin:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{psp_id, circuits: [{operation, state, requests, failures}]}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "circuits": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "operation": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "requests": {
                            "type": "integer"
                          },
                          "failures": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "circuits": [
                    {
                      "operation": "authorize",
                      "state": "closed",
                      "requests": 1284,
                      "failures": 3
                    },
                    {
                      "operation": "payout",
                      "state": "half-open",
                      "requests": 2,
                      "failures": 0
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/retry-policy": {
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "putRetryPolicy",
        "summary": "Tune retries, timeouts and circuits",
        "description": "A new version of the PSP's retry_policy (PSP-9.7): per pattern and optionally per transaction `type`, the conditions, `delays`, `max_attempts`, `on_exhausted`, a call `timeout` (1 s to 2 min) and, for status_query, the `window` (1 min to 72 h); `breaker` thresholds and the retry `budget`. Values outside the platform's limits are refused with the allowed range (`GET .../retry-policy` lists the limits). Versions are audited (`config.retry_policy.version_created`); a change to a money-moving pattern (outbound_sync, outbound_async) waits for KulPay's approval (202), any other is active at once (201). Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "policies": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "breaker": {
                    "type": "object"
                  },
                  "budget": {
                    "type": "object"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "policies": [
                  {}
                ],
                "breaker": {},
                "budget": {},
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Active at once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "activated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "approval": {}
                  }
                },
                "example": {
                  "version": {
                    "id": "3KXU64pBgOZqnJAQ4TXU6EIrN5F",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "kind": "retry_policy",
                    "version": 3,
                    "status": "active",
                    "reason": "Longer status-query window for overnight M-Pesa settlement",
                    "created_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                    "created_at": "2026-10-11T10:14:00Z",
                    "activated_at": "2026-10-11T10:14:00Z"
                  },
                  "approval": null
                }
              }
            }
          },
          "202": {
            "description": "Waiting for KulPay's approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "3Kpqk5QKsEzH7qwjFpKRodpoJ4G",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "kind": "retry_policy",
                    "version": 4,
                    "status": "pending",
                    "reason": "Fail over after two soft declines instead of three",
                    "approval_id": "3Ke5KHiog5p9N2Mxo29S5Szrbhy",
                    "created_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                    "created_at": "2026-10-11T10:20:00Z"
                  },
                  "approval": {
                    "id": "3Ke5KHiog5p9N2Mxo29S5Szrbhy",
                    "kind": "config.activate",
                    "subject_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "tenant_id": "3K12JLy9OnIwuN9yzoIgIKQpHJX",
                    "payload": "{\"version_id\":\"3Kpqk5QKsEzH7qwjFpKRodpoJ4G\",\"kind\":\"retry_policy\",\"version\":4}",
                    "reason": "retry_policy v4: Fail over after two soft declines instead of three",
                    "prepared_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                    "status": "pending",
                    "created_at": "2026-10-11T10:20:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Outside the limits; the allowed range is named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "retry policy: status_query: window \"96h\" must be between 1m0s and 72h0m0s; outbound_notification: max_attempts must be between 1 and 20 (0, until the window closes, only for status_query)",
                  "error": {
                    "code": "invalid_request",
                    "message": "retry policy: status_query: window \"96h\" must be between 1m0s and 72h0m0s; outbound_notification: max_attempts must be between 1 and 20 (0, until the window closes, only for status_query)"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getRetryPolicy",
        "summary": "A PSP's effective retry policies",
        "description": "Per interaction pattern (PSP-9.2): retryable and never-retried conditions, delays (with jitter), maximum attempts and the action after the last attempt (failover, status_query, dlq, fail) \u2014 the PSP's active retry_policy version where it overrides a pattern, else the platform default \u2014 and the platform's limits. An unknown outcome is never resent: it goes to status queries. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{psp_id, active_version, effective: [policy], overridden: [pattern], limits}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "active_version": {
                      "type": "integer"
                    },
                    "effective": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "pattern": {
                            "type": "string"
                          },
                          "retry_on": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "never_on": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "delays": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "jitter": {
                            "type": "integer"
                          },
                          "max_attempts": {
                            "type": "integer"
                          },
                          "on_exhausted": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "overridden": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "limits": {
                      "type": "object",
                      "properties": {
                        "min_delay": {
                          "type": "string"
                        },
                        "max_delay": {
                          "type": "string"
                        },
                        "max_total": {
                          "type": "string"
                        },
                        "max_attempts": {
                          "type": "integer"
                        },
                        "max_jitter": {
                          "type": "number"
                        },
                        "conditions": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "on_exhausted": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "active_version": 3,
                  "effective": [
                    {
                      "pattern": "outbound_sync",
                      "retry_on": [
                        "error_before_send",
                        "soft_decline"
                      ],
                      "never_on": [
                        "hard_decline",
                        "timeout"
                      ],
                      "delays": [
                        "0s"
                      ],
                      "jitter": 0,
                      "max_attempts": 3,
                      "on_exhausted": "fail"
                    },
                    {
                      "pattern": "outbound_async",
                      "retry_on": [
                        "error_before_send",
                        "soft_decline"
                      ],
                      "never_on": [
                        "hard_decline",
                        "timeout"
                      ],
                      "delays": [
                        "0s"
                      ],
                      "jitter": 0,
                      "max_attempts": 3,
                      "on_exhausted": "fail"
                    },
                    {
                      "pattern": "status_query",
                      "timeout": "30s",
                      "window": "48h",
                      "retry_on": [
                        "pending",
                        "timeout",
                        "connection_error",
                        "http_5xx",
                        "http_429"
                      ],
                      "delays": [
                        "10s",
                        "30s",
                        "2m",
                        "10m",
                        "1h"
                      ],
                      "jitter": 0.1,
                      "max_attempts": 0,
                      "on_exhausted": "dlq"
                    },
                    {
                      "pattern": "outbound_notification",
                      "retry_on": [
                        "timeout",
                        "connection_error",
                        "http_5xx",
                        "http_429",
                        "http_408"
                      ],
                      "never_on": [
                        "http_410",
                        "http_4xx"
                      ],
                      "delays": [
                        "1m",
                        "4m",
                        "10m",
                        "45m",
                        "2h",
                        "5h",
                        "16h"
                      ],
                      "jitter": 0.1,
                      "max_attempts": 8,
                      "on_exhausted": "dlq"
                    },
                    {
                      "pattern": "inbound_callback",
                      "retry_on": [
                        "timeout",
                        "connection_error",
                        "http_5xx"
                      ],
                      "delays": [
                        "30s",
                        "1m",
                        "2m",
                        "4m",
                        "8m",
                        "16m",
                        "32m"
                      ],
                      "jitter": 0.2,
                      "max_attempts": 8,
                      "on_exhausted": "dlq"
                    }
                  ],
                  "overridden": [
                    "status_query"
                  ],
                  "limits": {
                    "min_delay": "1s",
                    "max_delay": "24h0m0s",
                    "max_total": "72h0m0s",
                    "max_attempts": 20,
                    "max_jitter": 0.5,
                    "conditions": [
                      "error_before_send",
                      "timeout",
                      "connection_error",
                      "http_5xx",
                      "http_429",
                      "http_408",
                      "http_410",
                      "http_4xx",
                      "soft_decline",
                      "hard_decline",
                      "pending",
                      "unknown_outcome"
                    ],
                    "on_exhausted": [
                      "failover",
                      "status_query",
                      "dlq",
                      "fail"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/orientation": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getOrientation",
        "summary": "A PSP's orientation profile",
        "description": "The interaction patterns declared for each transaction type the capability matrix enables (PSP-7.1), e.g. SALE = outbound_async + inbound_callback + status_query, the profile the matrix suggests, and the pattern vocabulary. Every event carries its direction and pattern; certification runs one case per declared pattern. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{psp_id, active, active_version, suggested, patterns}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "active": {
                      "type": "object",
                      "properties": {
                        "types": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string"
                              },
                              "patterns": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "active_version": {
                      "type": "integer"
                    },
                    "suggested": {
                      "type": "object",
                      "properties": {
                        "types": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string"
                              },
                              "patterns": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "patterns": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "active": {
                    "types": [
                      {
                        "type": "SALE",
                        "patterns": [
                          "inbound_callback",
                          "outbound_async",
                          "status_query"
                        ]
                      },
                      {
                        "type": "PAYOUT",
                        "patterns": [
                          "outbound_async",
                          "status_query"
                        ]
                      }
                    ]
                  },
                  "active_version": 2,
                  "suggested": {
                    "types": [
                      {
                        "type": "SALE",
                        "patterns": [
                          "inbound_callback",
                          "outbound_async",
                          "status_query"
                        ]
                      },
                      {
                        "type": "PAYOUT",
                        "patterns": [
                          "inbound_callback",
                          "outbound_async",
                          "status_query"
                        ]
                      }
                    ]
                  },
                  "patterns": [
                    "outbound_sync",
                    "outbound_async",
                    "outbound_notification",
                    "inbound_request",
                    "inbound_callback",
                    "inbound_file",
                    "outbound_file",
                    "status_query"
                  ]
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "putOrientation",
        "summary": "Declare a PSP's orientation profile",
        "description": "A new version, active at once (event config.orientation_profile.version_created). Types must be enabled in the capability matrix; patterns from the vocabulary.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "types",
                  "reason"
                ],
                "properties": {
                  "types": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "type": {
                          "type": "string",
                          "example": "SALE"
                        },
                        "patterns": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "enum": [
                              "outbound_sync",
                              "outbound_async",
                              "outbound_notification",
                              "inbound_request",
                              "inbound_callback",
                              "inbound_file",
                              "outbound_file",
                              "status_query"
                            ]
                          }
                        }
                      }
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "types": [
                  {
                    "type": "SALE",
                    "patterns": [
                      "outbound_sync"
                    ]
                  }
                ],
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "activated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "3K7OyLy19t8ti6E5r103HLquECm",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "kind": "orientation_profile",
                    "version": 2,
                    "status": "active",
                    "reason": "SALE confirmed by callback, status query as fallback",
                    "created_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                    "created_at": "2026-10-11T10:31:00Z",
                    "activated_at": "2026-10-11T10:31:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid profile, each problem listed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "orientation profile: REFUND is not enabled in the capability matrix; SALE: unknown pattern callback (one of outbound_sync, outbound_async, outbound_notification, inbound_request, inbound_callback, inbound_file, outbound_file, status_query)",
                  "error": {
                    "code": "invalid_request",
                    "message": "orientation profile: REFUND is not enabled in the capability matrix; SALE: unknown pattern callback (one of outbound_sync, outbound_async, outbound_notification, inbound_request, inbound_callback, inbound_file, outbound_file, status_query)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payouts": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "createPayout",
        "summary": "Send a payout",
        "description": "Pays a mobile wallet (M-Pesa B2C, eMola) or account (PSP-6.7). With method `bank`, a bank account in Mozambique: `beneficiary.account` is its NIB (21 digits, check digits verified) and `beneficiary.name` the account holder; the payout joins the next batch the bank rail sends at a cut-off (10:00 and 14:00 Maputo time) as an ISO 20022 pain.001, stays `processing` until the bank answers, fails with the bank's reason (failure_code, and the ISO code in provider_code) if rejected, and succeeds when the bank debits it; `batch_id` and `end_to_end_id` name it in the bank's files. The amount leaves your available balance (GET /v1/balance) before anything is sent, under a lock so two payouts cannot spend the same money, and comes back if the payout fails, is rejected or is returned. Above the deployment's approval threshold (payouts.approval_threshold) the payout waits in `pending_approval` for a second person with `payouts:approve`; a risk review also holds it there. The risk stage (PSP-6.3) applies: a blocked beneficiary is refused (422 risk_declined). An answer lost after sending leaves it `processing` until a status query settles it; KulPay never guesses. States: created, pending_approval, processing, succeeded, failed, returned. Webhooks: payout.succeeded, payout.failed, payout.returned. Requires `payouts:write` and an `Idempotency-Key` header.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "currency",
                  "method",
                  "beneficiary"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "example": "150.00"
                  },
                  "currency": {
                    "type": "string",
                    "example": "MZN"
                  },
                  "method": {
                    "type": "string",
                    "example": "mpesa",
                    "description": "The rail: mpesa, emola, or bank (a NIB, by the bank payout rail)."
                  },
                  "description": {
                    "type": "string"
                  },
                  "beneficiary": {
                    "type": "object",
                    "properties": {
                      "msisdn": {
                        "type": "string"
                      },
                      "account": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string",
                        "description": "The name you expect (see POST /v1/name-lookups)."
                      }
                    }
                  }
                }
              },
              "examples": {
                "mobile": {
                  "summary": "M-Pesa",
                  "value": {
                    "amount": "300",
                    "currency": "MZN",
                    "method": "mpesa",
                    "beneficiary": {
                      "msisdn": "258841234567",
                      "name": "Joao"
                    },
                    "description": "invoice 9"
                  }
                },
                "bank": {
                  "summary": "Bank transfer (NIB)",
                  "value": {
                    "amount": "1500.50",
                    "currency": "MZN",
                    "method": "bank",
                    "beneficiary": {
                      "account": "000300010000001234585",
                      "name": "\u00c1guas de Maputo"
                    },
                    "description": "settlement 2026-10-11"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The payout (a retry with the same key returns the same payout).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "pending_approval",
                        "processing",
                        "succeeded",
                        "failed",
                        "returned"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "destination": {
                      "type": "string"
                    },
                    "destination_type": {
                      "type": "string",
                      "enum": [
                        "msisdn",
                        "account"
                      ]
                    },
                    "beneficiary_name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "connector_ref": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string",
                      "description": "Why it failed, in KulPay's vocabulary (the same as payments' and refunds'): see /docs/errors#outcomes."
                    },
                    "failure_msg": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "provider_code": {
                      "type": "string",
                      "description": "The PSP's own answer, as it gave it."
                    },
                    "code_family": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "soft_decline",
                        "hard_decline",
                        "pending",
                        "unknown",
                        "error"
                      ]
                    },
                    "batch_id": {
                      "type": "string",
                      "description": "With method `bank`: the batch (pain.001) that carries it."
                    },
                    "bank_code": {
                      "type": "string",
                      "description": "With method `bank`: the receiving bank, the NIB's first four digits."
                    },
                    "end_to_end_id": {
                      "type": "string",
                      "description": "With method `bank`: the EndToEndId the bank reports it by."
                    },
                    "expected_execution_date": {
                      "type": "string",
                      "format": "date-time",
                      "description": "With method `bank`: the business day the bank should pay it (the date of the next cut-off; weekends and Mozambique's public holidays are skipped)."
                    }
                  }
                },
                "example": {
                  "id": "3KWPobOie4IrltWBWASMFIESXUk",
                  "account_id": "3KWPoS1bR2IZiR2ksqOoozIDdc2",
                  "mode": "test",
                  "amount": "300",
                  "currency": "MZN",
                  "status": "succeeded",
                  "destination": "258841234567",
                  "method": "mpesa",
                  "destination_type": "msisdn",
                  "beneficiary_name": "Joao",
                  "description": "invoice 9",
                  "connector_name": "mock_mpesa",
                  "connector_ref": "po_3KWPobOie4IrltWBWASMFIESXUk",
                  "created_by": "key_payouts",
                  "submitted_at": "2026-10-10T22:15:26.839332751Z",
                  "completed_at": "2026-10-10T22:15:26.873505789Z",
                  "created_at": "2026-10-10T22:15:26.792088415Z",
                  "updated_at": "2026-10-10T22:15:26.873505789Z",
                  "deleted_at": null
                }
              }
            }
          },
          "400": {
            "description": "Invalid amount, no beneficiary, no Idempotency-Key; for method bank, an invalid NIB (`invalid_account`) or no beneficiary name (`beneficiary_name_required`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "idempotency_key_required",
                  "detail": "This request moves money: send an Idempotency-Key header so a retry can never run it twice.",
                  "error": {
                    "code": "idempotency_key_required",
                    "message": "This request moves money: send an Idempotency-Key header so a retry can never run it twice."
                  }
                }
              }
            }
          },
          "422": {
            "description": "`insufficient_balance`, `not_supported` (the method cannot pay out) or `risk_declined`. `pilot_limit` while the account is a PSP in pilot: the live transaction breaks the pilot setting named in `rule` (`per_transaction`, `daily`, `monthly`, `currency`, `settings_missing`). Daily and monthly limits count payments, payouts, bill payments and deposits together. `limit_exceeded` with `rule` `beneficiary_daily` when a bank payout would take one NIB past the bank rail's daily limit (bank_rail.beneficiary_daily_limit).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Insufficient balance",
                  "status": 422,
                  "code": "insufficient_balance",
                  "detail": "The available balance does not cover this payout.",
                  "error": {
                    "code": "insufficient_balance",
                    "message": "The available balance does not cover this payout."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "listPayouts",
        "summary": "List payouts",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "destination": {
                            "type": "string"
                          },
                          "method": {
                            "type": "string"
                          },
                          "destination_type": {
                            "type": "string"
                          },
                          "connector_name": {
                            "type": "string"
                          },
                          "connector_ref": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "approved_by": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "submitted_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "completed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "deleted_at": {}
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPog5AqNigZC2DABGSG1qEabV",
                      "account_id": "3KWPoS1bR2IZiR2ksqOoozIDdc2",
                      "mode": "test",
                      "amount": "600",
                      "currency": "MZN",
                      "status": "succeeded",
                      "destination": "258841234567",
                      "method": "mpesa",
                      "destination_type": "msisdn",
                      "connector_name": "mock_mpesa",
                      "connector_ref": "po_3KWPog5AqNigZC2DABGSG1qEabV",
                      "approval_id": "3KWPoiCe7qzdyB7tV5IdxdJ3W8B",
                      "approved_by": "key_approver",
                      "created_by": "key_payouts",
                      "submitted_at": "2026-10-10T22:15:27.093792703Z",
                      "completed_at": "2026-10-10T22:15:27.115651007Z",
                      "created_at": "2026-10-10T22:15:27.016275765Z",
                      "updated_at": "2026-10-10T22:15:27.115651007Z",
                      "deleted_at": null
                    },
                    {
                      "id": "3KWPobOie4IrltWBWASMFIESXUk",
                      "account_id": "3KWPoS1bR2IZiR2ksqOoozIDdc2",
                      "mode": "test",
                      "amount": "300",
                      "currency": "MZN",
                      "status": "succeeded",
                      "destination": "258841234567",
                      "method": "mpesa",
                      "destination_type": "msisdn",
                      "beneficiary_name": "Joao",
                      "description": "invoice 9",
                      "connector_name": "mock_mpesa",
                      "connector_ref": "po_3KWPobOie4IrltWBWASMFIESXUk",
                      "created_by": "key_payouts",
                      "submitted_at": "2026-10-10T22:15:26.839332751Z",
                      "completed_at": "2026-10-10T22:15:26.873505789Z",
                      "created_at": "2026-10-10T22:15:26.792088415Z",
                      "updated_at": "2026-10-10T22:15:26.873505789Z",
                      "deleted_at": null
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/payouts/{id}": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "getPayout",
        "summary": "Get a payout",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payout.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "pending_approval",
                        "processing",
                        "succeeded",
                        "failed",
                        "returned"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "destination": {
                      "type": "string"
                    },
                    "destination_type": {
                      "type": "string",
                      "enum": [
                        "msisdn",
                        "account"
                      ]
                    },
                    "beneficiary_name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "connector_ref": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string",
                      "description": "Why it failed, in KulPay's vocabulary (the same as payments' and refunds'): see /docs/errors#outcomes."
                    },
                    "failure_msg": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "provider_code": {
                      "type": "string",
                      "description": "The PSP's own answer, as it gave it."
                    },
                    "code_family": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "soft_decline",
                        "hard_decline",
                        "pending",
                        "unknown",
                        "error"
                      ]
                    }
                  }
                },
                "example": {
                  "id": "3KWPobOie4IrltWBWASMFIESXUk",
                  "account_id": "3KWPoS1bR2IZiR2ksqOoozIDdc2",
                  "mode": "test",
                  "amount": "300",
                  "currency": "MZN",
                  "status": "succeeded",
                  "destination": "258841234567",
                  "method": "mpesa",
                  "destination_type": "msisdn",
                  "beneficiary_name": "Joao",
                  "description": "invoice 9",
                  "connector_name": "mock_mpesa",
                  "connector_ref": "po_3KWPobOie4IrltWBWASMFIESXUk",
                  "created_by": "key_payouts",
                  "submitted_at": "2026-10-10T22:15:26.839332751Z",
                  "completed_at": "2026-10-10T22:15:26.873505789Z",
                  "created_at": "2026-10-10T22:15:26.792088415Z",
                  "updated_at": "2026-10-10T22:15:26.873505789Z",
                  "deleted_at": null
                }
              }
            }
          },
          "404": {
            "description": "No such payout for this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "payout not found",
                  "error": {
                    "code": "not_found",
                    "message": "payout not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payouts/{id}/approve": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "approvePayout",
        "summary": "Approve a payout",
        "description": "The second person on a payout waiting for approval; it is submitted at once. The creator cannot approve their own. Requires `payouts:approve`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "checked"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payout after submission.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "destination": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "destination_type": {
                      "type": "string"
                    },
                    "connector_name": {
                      "type": "string"
                    },
                    "connector_ref": {
                      "type": "string"
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "deleted_at": {}
                  }
                },
                "example": {
                  "id": "3KWPog5AqNigZC2DABGSG1qEabV",
                  "account_id": "3KWPoS1bR2IZiR2ksqOoozIDdc2",
                  "mode": "test",
                  "amount": "600",
                  "currency": "MZN",
                  "status": "succeeded",
                  "destination": "258841234567",
                  "method": "mpesa",
                  "destination_type": "msisdn",
                  "connector_name": "mock_mpesa",
                  "connector_ref": "po_3KWPog5AqNigZC2DABGSG1qEabV",
                  "approval_id": "3KWPoiCe7qzdyB7tV5IdxdJ3W8B",
                  "approved_by": "key_approver",
                  "created_by": "key_payouts",
                  "submitted_at": "2026-10-10T22:15:27.093792703Z",
                  "completed_at": "2026-10-10T22:15:27.115651007Z",
                  "created_at": "2026-10-10T22:15:27.016275765Z",
                  "updated_at": "2026-10-10T22:15:27.115651007Z",
                  "deleted_at": null
                }
              }
            }
          },
          "400": {
            "description": "The body is not JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "body must be JSON: unexpected EOF",
                  "error": {
                    "code": "invalid_request",
                    "message": "body must be JSON: unexpected EOF"
                  }
                }
              }
            }
          },
          "403": {
            "description": "`same_approver`: whoever created the payout cannot approve it (four eyes).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "same_approver",
                  "detail": "the person who created the payout cannot approve it",
                  "error": {
                    "code": "same_approver",
                    "message": "the person who created the payout cannot approve it"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invalid_state`: the payout is not waiting for approval (already decided, or never needed it).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "the payout is not waiting for approval",
                  "error": {
                    "code": "invalid_state",
                    "message": "the payout is not waiting for approval"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payouts/{id}/reject": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "rejectPayout",
        "summary": "Reject a payout",
        "description": "Fails a payout waiting for approval; the amount comes back to the available balance. Requires `payouts:approve`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The failed payout.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "destination": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "destination_type": {
                      "type": "string"
                    },
                    "beneficiary_name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "failure_code": {
                      "type": "string",
                      "description": "Why it failed, in KulPay's vocabulary (the same as payments' and refunds'): see /docs/errors#outcomes."
                    },
                    "failure_msg": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "deleted_at": {},
                    "provider_code": {
                      "type": "string",
                      "description": "The PSP's own answer, as it gave it."
                    },
                    "code_family": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "soft_decline",
                        "hard_decline",
                        "pending",
                        "unknown",
                        "error"
                      ]
                    }
                  }
                },
                "example": {
                  "id": "3KmID0XKAlrxxCdU4SeehnmmDs8",
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "mode": "live",
                  "amount": "15000",
                  "currency": "MZN",
                  "status": "failed",
                  "destination": "258841234567",
                  "method": "mpesa",
                  "destination_type": "msisdn",
                  "beneficiary_name": "Ana Macu\u00e1cua",
                  "description": "Pagamento a fornecedor",
                  "approval_id": "3KJobAemOZPNz4LOibuGrDbqqB5",
                  "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                  "completed_at": "2026-10-11T10:04:18Z",
                  "failure_code": "rejected",
                  "failure_msg": "Rejected by an approver.",
                  "created_at": "2026-10-11T09:47:02Z",
                  "updated_at": "2026-10-11T10:04:18Z",
                  "deleted_at": null
                }
              }
            }
          },
          "400": {
            "description": "The body is not JSON.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "body must be JSON: unexpected EOF",
                  "error": {
                    "code": "invalid_request",
                    "message": "body must be JSON: unexpected EOF"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invalid_state`: the payout is not waiting for approval (already decided, or never needed it).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "the payout is not waiting for approval",
                  "error": {
                    "code": "invalid_state",
                    "message": "the payout is not waiting for approval"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "getBalance",
        "summary": "Available balance",
        "description": "What payouts can draw on: settled charges less fees, refunds and payouts, in one currency. Requires `payouts:read`. `reserved` is the rolling reserve the partner's terms hold back (F2-6), apart from `available`. Each release of reserve holds that fell due is announced by the `reserve.released` webhook.",
        "parameters": [
          {
            "name": "currency",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "MZN"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{currency, mode, available}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "reserved": {
                      "type": "string",
                      "description": "Held in the rolling reserve, not available until released (F2-6)."
                    }
                  }
                },
                "example": {
                  "available": "1020",
                  "currency": "MZN",
                  "mode": "test",
                  "reserved": "0"
                }
              }
            }
          }
        }
      }
    },
    "/v1/ledger/accounts": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listLedgerAccounts",
        "summary": "Chart of accounts and balances",
        "description": "The account's double-entry ledger (PSP-10.1): one chart per mode and currency (client_funds, merchant_payable, merchant_available, merchant_pending, fee_revenue, psp_receivable, psp_fees_payable, reserves, suspense, fx_adjustment, rounding_adjustment), each with running `debits_minor`, `credits_minor` and `balance_minor` on its normal side. Running totals are verified nightly against the journal lines. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [ledger account], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "normal": {
                            "type": "string"
                          },
                          "debits_minor": {
                            "type": "integer"
                          },
                          "credits_minor": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "balance_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K1QnKiD3oyu03HYXKLfzzdD7ta",
                      "tenant_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "mode": "live",
                      "currency": "MZN",
                      "code": "merchant_available",
                      "name": "Merchant available balance",
                      "normal": "credit",
                      "debits_minor": 1250000,
                      "credits_minor": 4581524,
                      "created_at": "2026-02-10T09:00:00Z",
                      "updated_at": "2026-10-11T10:12:44Z",
                      "balance_minor": 3331524
                    },
                    {
                      "id": "3K2OzaB1FMyyVeCj2X0ub9kvk9Q",
                      "tenant_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "mode": "live",
                      "currency": "MZN",
                      "code": "client_funds",
                      "name": "Client funds (safeguarding)",
                      "normal": "debit",
                      "debits_minor": 9875025,
                      "credits_minor": 5543501,
                      "created_at": "2026-02-10T09:00:00Z",
                      "updated_at": "2026-10-11T10:12:44Z",
                      "balance_minor": 4331524
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/ledger/journals": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listLedgerJournals",
        "summary": "Journals",
        "description": "Balanced journals, newest first. Each links to its transaction (`transaction_id`) and to the `ledger.journal.posted` event recorded with it (`event_id`); a reversing journal names the one it corrects (`reverses_id`). Journals are append-only. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "transaction_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [journal], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "transaction_id": {
                            "type": "string"
                          },
                          "event_id": {
                            "type": "string"
                          },
                          "entry_id": {
                            "type": "string"
                          },
                          "line_count": {
                            "type": "integer"
                          },
                          "total_minor": {
                            "type": "integer"
                          },
                          "posted_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUYP7LAu4BNE1OQFHhUy5WsJC8",
                      "tenant_id": "3KUYB1xNg7nZq4ACrTtGk8IeVo4",
                      "mode": "live",
                      "currency": "MZN",
                      "kind": "fee",
                      "transaction_id": "3KUYH1DSm7tFw4GIxZzMq8OkBu0",
                      "event_id": "3KUYQ1MBv7COF4PRGIiVz8XtKD9",
                      "entry_id": "3KUYR4NCw1DPG7QSHJjWA2YuLE0",
                      "line_count": 2,
                      "total_minor": 3127,
                      "posted_at": "2026-10-10T14:32:09Z"
                    },
                    {
                      "id": "3KUYS7ODx4EQH1RTIKkXB5ZvMF1",
                      "tenant_id": "3KUYB1xNg7nZq4ACrTtGk8IeVo4",
                      "mode": "live",
                      "currency": "MZN",
                      "kind": "charge",
                      "transaction_id": "3KUYH1DSm7tFw4GIxZzMq8OkBu0",
                      "event_id": "3KUYT1PEy7FRI4SUJLlYC8AwNG2",
                      "entry_id": "3KUYU4QFz1GSJ7TVKMmZD2BxOH3",
                      "line_count": 2,
                      "total_minor": 125075,
                      "posted_at": "2026-10-10T14:32:09Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/ledger/journals/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getLedgerJournal",
        "summary": "A journal with its lines",
        "description": "A journal and its lines: each line debits or credits one ledger account; debits equal credits. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The journal with `lines`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "transaction_id": {
                      "type": "string"
                    },
                    "event_id": {
                      "type": "string"
                    },
                    "entry_id": {
                      "type": "string"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "total_minor": {
                      "type": "integer"
                    },
                    "posted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "journal_id": {
                            "type": "string"
                          },
                          "ledger_account_id": {
                            "type": "string"
                          },
                          "account_code": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "debit_minor": {
                            "type": "integer"
                          },
                          "credit_minor": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KUYS7ODx4EQH1RTIKkXB5ZvMF1",
                  "tenant_id": "3KUYB1xNg7nZq4ACrTtGk8IeVo4",
                  "mode": "live",
                  "currency": "MZN",
                  "kind": "charge",
                  "transaction_id": "3KUYH1DSm7tFw4GIxZzMq8OkBu0",
                  "event_id": "3KUYT1PEy7FRI4SUJLlYC8AwNG2",
                  "entry_id": "3KUYU4QFz1GSJ7TVKMmZD2BxOH3",
                  "line_count": 2,
                  "total_minor": 125075,
                  "posted_at": "2026-10-10T14:32:09Z",
                  "lines": [
                    {
                      "id": "3KUYV7RGA4HTK1UWLNnAE5CyPI4",
                      "journal_id": "3KUYS7ODx4EQH1RTIKkXB5ZvMF1",
                      "ledger_account_id": "3KUYW1SHB7IUL4VXMOoBF8DzQJ5",
                      "account_code": "psp_receivable",
                      "currency": "MZN",
                      "debit_minor": 125075,
                      "credit_minor": 0,
                      "created_at": "2026-10-10T14:32:09Z"
                    },
                    {
                      "id": "3KUYX4TIC1JVM7WYNPpCG2EAaK6",
                      "journal_id": "3KUYS7ODx4EQH1RTIKkXB5ZvMF1",
                      "ledger_account_id": "3KUYY7UJD4KWN1XZOQqDH5FBbL7",
                      "account_code": "merchant_pending",
                      "currency": "MZN",
                      "debit_minor": 0,
                      "credit_minor": 125075,
                      "created_at": "2026-10-10T14:32:09Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such journal.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such journal",
                  "error": {
                    "code": "not_found",
                    "message": "no such journal"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/ledger/journals/{id}/reverse": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "reverseLedgerJournal",
        "summary": "Correct a journal by reversing it",
        "description": "Posts the mirror image of a journal, referencing it (`reverses_id`), with the reason as its memo; journals are never edited or deleted. A journal is reversed at most once and a reversal is not itself reversed. The available balance moves with it. Records `ledger.journal.posted`. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The reversing journal.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "transaction_id": {
                      "type": "string"
                    },
                    "event_id": {
                      "type": "string"
                    },
                    "reverses_id": {
                      "type": "string"
                    },
                    "entry_id": {
                      "type": "string"
                    },
                    "memo": {
                      "type": "string"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "total_minor": {
                      "type": "integer"
                    },
                    "posted_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K7ZnIEvw02DpWLConn6DR7kTKw",
                  "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "mode": "live",
                  "currency": "MZN",
                  "kind": "journal_reversal",
                  "transaction_id": "3KKflkCic0KwrGzM5hzGTfngNiH",
                  "event_id": "3KtGTPT0AQaKTIWS8Xv6tRyJwCs",
                  "reverses_id": "3K1zixgG7mZ8P6eT8Gua1XCk5Se",
                  "entry_id": "3K3YD5LnG4uFl4lRYgi3k6rEyrm",
                  "memo": "Taxa lan\u00e7ada em duplicado na cobran\u00e7a de 9 de outubro",
                  "line_count": 2,
                  "total_minor": 4250,
                  "posted_at": "2026-10-11T10:20:05Z"
                }
              }
            }
          },
          "404": {
            "description": "No such journal.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such journal",
                  "error": {
                    "code": "not_found",
                    "message": "no such journal"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Already reversed, or itself a reversal (`not_reversible`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_reversible",
                  "detail": "journal 3K1zixgG7mZ8P6eT8Gua1XCk5Se is already reversed",
                  "error": {
                    "code": "not_reversible",
                    "message": "journal 3K1zixgG7mZ8P6eT8Gua1XCk5Se is already reversed"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/funds-flow": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getFundsFlow",
        "summary": "A merchant's funds-flow model",
        "description": "PSP-10.2. `override` is the merchant's own model (collecting or direct); empty follows each PSP's approved commercial terms, and `default` (collecting) applies where neither sets one. `pending` is a requested change awaiting its checker. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{account_id, override, default, pending}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "default": {
                      "type": "string"
                    },
                    "pending": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "default": "collecting",
                  "pending": {
                    "id": "3KJobAemOZPNz4LOibuGrDbqqB5",
                    "kind": "funds_flow.change",
                    "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "payload": "{\"account_id\":\"3KY8VJtMMG2V1cNjNhTgPt1mSFI\",\"funds_flow\":\"direct\",\"previous\":\"\"}",
                    "reason": "O PSP passa a liquidar diretamente ao comerciante (adenda contratual de outubro)",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-09T11:20:31Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such account",
                  "error": {
                    "code": "not_found",
                    "message": "no such account"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Ledger"
        ],
        "operationId": "putFundsFlow",
        "summary": "Request a funds-flow change",
        "description": "collecting: PSP to KulPay's safeguarding account to the merchant, with the client-funds liability and payouts in the ledger. direct: PSP to the merchant; KulPay keeps a shadow ledger of what the merchant should receive and the fees it is owed, outside the available balance. The change waits for a second person (`POST /v1/admin/approvals/{id}/decide`) and then applies to new payments only; each payment keeps the model in force when its PSP was chosen. Records `config.funds_flow.changed`. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "funds_flow",
                  "reason"
                ],
                "properties": {
                  "funds_flow": {
                    "type": "string",
                    "enum": [
                      "",
                      "collecting",
                      "direct"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "funds_flow": "",
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KJobAemOZPNz4LOibuGrDbqqB5",
                    "kind": "funds_flow.change",
                    "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "payload": "{\"account_id\":\"3KY8VJtMMG2V1cNjNhTgPt1mSFI\",\"funds_flow\":\"direct\",\"previous\":\"\"}",
                    "reason": "O PSP passa a liquidar diretamente ao comerciante (adenda contratual de outubro)",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-09T11:20:31Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown model, no change, or a change already pending.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a funds-flow change is already waiting for approval (3KJobAemOZPNz4LOibuGrDbqqB5)",
                  "error": {
                    "code": "invalid_request",
                    "message": "a funds-flow change is already waiting for approval (3KJobAemOZPNz4LOibuGrDbqqB5)"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such account",
                  "error": {
                    "code": "not_found",
                    "message": "no such account"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/fee-schedules": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listFeeSchedules",
        "summary": "Fee schedule versions",
        "description": "Versions of PSP and merchant fee schedules, newest first (PSP-10.5). Requires `accounts:read`.",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "psp",
                "merchant"
              ]
            }
          },
          {
            "name": "subject_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [schedule], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "scope": {
                            "type": "string"
                          },
                          "subject_id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "document": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "approved_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "activated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KVJAYtz9ILvHWq4UNOkbO8klRW",
                      "scope": "merchant",
                      "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "version": 2,
                      "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "document": "{\"rules\":[{\"type\":\"SALE\",\"method\":\"mpesa\",\"currency\":\"MZN\",\"fixed\":\"5\",\"percent\":\"1.5\",\"min_fee\":\"10\",\"max_fee\":\"500\"}]}",
                      "status": "active",
                      "reason": "Tarifa negociada para 2026",
                      "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                      "approval_id": "3KJslQCJSqDHZTJwZ7NMOHDLvqV",
                      "approved_by": "operator:checker@example.co.mz",
                      "created_at": "2026-02-03T10:00:00Z",
                      "activated_at": "2026-02-03T16:22:09Z"
                    },
                    {
                      "id": "3KTWhTkMF6ror2SF88hNezhwVi8",
                      "scope": "psp",
                      "subject_id": "3KtGTPT0AQaKTIWS8Xv6tRyJwCs",
                      "version": 1,
                      "tenant_id": "3K75aZx53yJUW0o5JRyLpwMufMq",
                      "document": "{\"rules\":[{\"currency\":\"MZN\",\"percent\":\"2\"}]}",
                      "status": "active",
                      "reason": "Tabela padr\u00e3o do PSP",
                      "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                      "approval_id": "3Kh8Qx4H3yHpjhVVX645vFcRXuU",
                      "approved_by": "operator:checker@example.co.mz",
                      "created_at": "2026-01-12T08:30:00Z",
                      "activated_at": "2026-01-12T09:10:41Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "proposeFeeSchedule",
        "summary": "Propose a fee schedule version",
        "description": "An ordered list of rules for a PSP or a merchant (PSP-10.5); the first rule matching the transaction type, method, currency and amount band prices it: `fixed`, `percent` or both, bounded by `min_fee` and `max_fee`, with `tiers` by the merchant's monthly volume replacing the fixed and percentage parts. Fees are worked out at posting time in decimal arithmetic, rounded with the currency's rule; the merchant's schedule applies first, then its PSP's, then the platform default. The version waits for a second person (`POST /v1/admin/approvals/{id}/decide`); events `config.fee_schedule.version_created` and `config.fee_schedule.activated`. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scope",
                  "subject_id",
                  "rules",
                  "reason"
                ],
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "psp",
                      "merchant"
                    ]
                  },
                  "subject_id": {
                    "type": "string"
                  },
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "currency"
                      ],
                      "properties": {
                        "type": {
                          "type": "string",
                          "description": "Transaction type (SALE, ...); empty or * for any."
                        },
                        "method": {
                          "type": "string",
                          "description": "Empty or * for any."
                        },
                        "currency": {
                          "type": "string"
                        },
                        "min_amount": {
                          "type": "string",
                          "description": "Inclusive."
                        },
                        "max_amount": {
                          "type": "string",
                          "description": "Exclusive."
                        },
                        "fixed": {
                          "type": "string"
                        },
                        "percent": {
                          "type": "string"
                        },
                        "min_fee": {
                          "type": "string"
                        },
                        "max_fee": {
                          "type": "string"
                        },
                        "tiers": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "monthly_volume_from"
                            ],
                            "properties": {
                              "monthly_volume_from": {
                                "type": "string"
                              },
                              "fixed": {
                                "type": "string"
                              },
                              "percent": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "scope": "psp",
                "subject_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "rules": [
                  {
                    "type": "standard",
                    "method": "string",
                    "currency": "MZN",
                    "min_amount": "1250.00",
                    "max_amount": "1250.00",
                    "fixed": "string",
                    "percent": "string",
                    "min_fee": "1250.00",
                    "max_fee": "1250.00",
                    "tiers": [
                      {
                        "monthly_volume_from": "string",
                        "fixed": "string",
                        "percent": "string"
                      }
                    ]
                  }
                ],
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{schedule, approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedule": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "scope": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "document": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "schedule": {
                    "id": "3KJslQCJSqDHZTJwZ7NMOHDLvqV",
                    "scope": "merchant",
                    "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "version": 3,
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "document": "{\"rules\":[{\"type\":\"SALE\",\"method\":\"mpesa\",\"currency\":\"MZN\",\"fixed\":\"5\",\"percent\":\"1.25\",\"min_fee\":\"10\",\"max_fee\":\"450\"}]}",
                    "status": "pending",
                    "reason": "Revis\u00e3o de tarifa ap\u00f3s aumento de volume",
                    "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "approval_id": "3KMuWRw3JaLEf2SolKp9TuBxIGy",
                    "created_at": "2026-10-11T08:12:55Z"
                  },
                  "approval": {
                    "id": "3KMuWRw3JaLEf2SolKp9TuBxIGy",
                    "kind": "fee_schedule.activate",
                    "subject_id": "3KJslQCJSqDHZTJwZ7NMOHDLvqV",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "payload": "{\"schedule_id\":\"3KJslQCJSqDHZTJwZ7NMOHDLvqV\",\"version\":3}",
                    "reason": "Revis\u00e3o de tarifa ap\u00f3s aumento de volume",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-11T08:12:55Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A rule is invalid, or a version is already pending; the problems are named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "rules[0].percent must be at most 100; rules[1].max_fee must be at least min_fee",
                  "error": {
                    "code": "invalid_request",
                    "message": "rules[0].percent must be at most 100; rules[1].max_fee must be at least min_fee"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such PSP or merchant.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such PSP or merchant",
                  "error": {
                    "code": "not_found",
                    "message": "no such PSP or merchant"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/fee-schedules/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getFeeSchedule",
        "summary": "A fee schedule version",
        "description": "Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "document": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "activated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KVJAYtz9ILvHWq4UNOkbO8klRW",
                  "scope": "merchant",
                  "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "version": 2,
                  "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "document": "{\"rules\":[{\"type\":\"SALE\",\"method\":\"mpesa\",\"currency\":\"MZN\",\"fixed\":\"5\",\"percent\":\"1.5\",\"min_fee\":\"10\",\"max_fee\":\"500\"}]}",
                  "status": "active",
                  "reason": "Tarifa negociada para 2026",
                  "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                  "approval_id": "3KJslQCJSqDHZTJwZ7NMOHDLvqV",
                  "approved_by": "operator:checker@example.co.mz",
                  "created_at": "2026-02-03T10:00:00Z",
                  "activated_at": "2026-02-03T16:22:09Z"
                }
              }
            }
          },
          "404": {
            "description": "No such schedule.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such fee schedule",
                  "error": {
                    "code": "not_found",
                    "message": "no such fee schedule"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/fee-schedules/quote": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "quoteFee",
        "summary": "Price a transaction",
        "description": "What a transaction would pay with the schedules in force, with the breakdown each payment records (`fee_breakdown`: scope, schedule version, rule, tier, parts, minimum or maximum applied). Requires `accounts:read`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account_id",
                  "currency",
                  "amount"
                ],
                "properties": {
                  "account_id": {
                    "type": "string"
                  },
                  "psp_id": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  },
                  "method": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "mode": "test",
                "type": "standard",
                "method": "string",
                "currency": "MZN",
                "amount": "1250.00"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{fee, currency, breakdown}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fee": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "scope": {
                          "type": "string"
                        },
                        "schedule_id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "rule": {
                          "type": "integer"
                        },
                        "fixed": {
                          "type": "string"
                        },
                        "percent": {
                          "type": "string"
                        },
                        "percent_part": {
                          "type": "string"
                        },
                        "fee": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "fee": "42.5",
                  "currency": "MZN",
                  "breakdown": {
                    "scope": "merchant",
                    "schedule_id": "3KVJAYtz9ILvHWq4UNOkbO8klRW",
                    "version": 2,
                    "rule": 0,
                    "fixed": "5",
                    "percent": "1.5",
                    "percent_part": "37.5",
                    "fee": "42.5"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid input.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "account_id, a positive amount and a 3-letter currency are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "account_id, a positive amount and a 3-letter currency are required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/settlements": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listSettlements",
        "summary": "Settlement batches",
        "description": "The account's settlement batches, newest first (PSP-10.3). At each cycle (T+n, `settlement_cycle_days`), the ledger movements up to the cut-off that no batch holds form a batch per mode, currency and funds-flow model: `gross` (charges), `fees`, `refunds` and `adjustments` (reversals, chargebacks, manual payouts, carried amounts), and `net`. Collecting model: the net is paid to the settlement destination (`paid`; `paying` while a bank transfer waits for the bank), or carried into the next batch when it cannot be (`failed`, `carried_forward`). Direct model: `recorded`, what the PSP should pay and the fees owed. Webhooks: settlement.batch.created, settlement.batch.paid, settlement.batch.failed. Requires `settlements:read`. Cash deposits credited on their confirmation (the PSP's credit_on confirmation) are settled only once the statement shows them (IO-6.1): one waiting for its statement line, or whose line differs or never came while its exception is open, is held back and goes in the batch after it is reconciled or resolved. The report lists each deposit by its deposit ID (transaction_id).",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [batch], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "funds_flow": {
                            "type": "string"
                          },
                          "cycle_date": {
                            "type": "string"
                          },
                          "cycle_days": {
                            "type": "integer"
                          },
                          "period_end": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "gross": {
                            "type": "string"
                          },
                          "fees": {
                            "type": "string"
                          },
                          "refunds": {
                            "type": "string"
                          },
                          "adjustments": {
                            "type": "string"
                          },
                          "net": {
                            "type": "string"
                          },
                          "line_count": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          },
                          "payout_id": {
                            "type": "string"
                          },
                          "paid_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "bank_reference": {
                            "type": "string",
                            "description": "When the bank rail paid it: the transfer's EndToEndId, as the bank's statement shows it."
                          },
                          "reserve": {
                            "type": "string",
                            "description": "The rolling reserve held (negative) and released (positive) in the period (F2-6)."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3Kvzd6L2Pcs92PdmK3dW77IpqDS",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "currency": "MZN",
                      "funds_flow": "collecting",
                      "cycle_date": "2026-10-10",
                      "cycle_days": 1,
                      "period_end": "2026-10-09T00:00:00Z",
                      "gross": "187450.75",
                      "fees": "2811.76",
                      "refunds": "1250.75",
                      "adjustments": "0",
                      "net": "183388.24",
                      "line_count": 143,
                      "status": "paid",
                      "payout_id": "3KZt4Xw9QmRb2NcV7pLs5YhEdJf",
                      "paid_at": "2026-10-10T10:02:44Z",
                      "created_at": "2026-10-10T02:00:05Z",
                      "updated_at": "2026-10-10T10:02:44Z"
                    },
                    {
                      "id": "3KugoejTSN0O7wRyLqNPHNR10Qx",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "currency": "MZN",
                      "funds_flow": "direct",
                      "cycle_date": "2026-10-10",
                      "cycle_days": 1,
                      "period_end": "2026-10-09T00:00:00Z",
                      "gross": "96200.5",
                      "fees": "1443.01",
                      "refunds": "0",
                      "adjustments": "0",
                      "net": "94757.49",
                      "line_count": 61,
                      "status": "recorded",
                      "verification": "verified",
                      "created_at": "2026-10-10T02:00:05Z",
                      "updated_at": "2026-10-10T06:05:12Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/settlements/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getSettlement",
        "summary": "A settlement batch with its lines",
        "description": "Every line: the ledger entry (or carried batch) it settles, its kind and bucket (gross, fees, refunds, adjustments), the transaction and the amount. A batch never changes once built. Requires `settlements:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{batch, lines}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batch": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "account_id": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "funds_flow": {
                          "type": "string"
                        },
                        "cycle_date": {
                          "type": "string"
                        },
                        "cycle_days": {
                          "type": "integer"
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "gross": {
                          "type": "string"
                        },
                        "fees": {
                          "type": "string"
                        },
                        "refunds": {
                          "type": "string"
                        },
                        "adjustments": {
                          "type": "string"
                        },
                        "net": {
                          "type": "string"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "payout_id": {
                          "type": "string"
                        },
                        "paid_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "bank_reference": {
                          "type": "string",
                          "description": "When the bank rail paid it: the transfer's EndToEndId, as the bank's statement shows it."
                        },
                        "reserve": {
                          "type": "string",
                          "description": "The rolling reserve held (negative) and released (positive) in the period (F2-6)."
                        }
                      }
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "batch_id": {
                            "type": "string"
                          },
                          "ref": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "entry_id": {
                            "type": "string"
                          },
                          "transaction_id": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "occurred_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "bucket": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "batch": {
                    "id": "3KBsl1R1SZjAG7lN66md59RgpOh",
                    "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                    "mode": "live",
                    "currency": "MZN",
                    "funds_flow": "collecting",
                    "cycle_date": "2026-10-10",
                    "cycle_days": 1,
                    "period_end": "2026-10-09T00:00:00Z",
                    "gross": "3400.25",
                    "fees": "-85.01",
                    "refunds": "0",
                    "adjustments": "0",
                    "net": "3315.24",
                    "line_count": 2,
                    "status": "paid",
                    "payout_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "paid_at": "2026-10-10T08:30:15Z",
                    "created_at": "2026-10-10T06:00:02Z",
                    "updated_at": "2026-10-10T08:30:15Z"
                  },
                  "lines": [
                    {
                      "id": "3K3eILCnMrCFupEJAC2iMgj0ZSg",
                      "batch_id": "3KBsl1R1SZjAG7lN66md59RgpOh",
                      "ref": "entry:3KiPDnFE3Q9HIZp1ALEj9Q5ELK4",
                      "kind": "charge",
                      "entry_id": "3KiPDnFE3Q9HIZp1ALEj9Q5ELK4",
                      "transaction_id": "3KjLY7BV6lTzyJvGXr3mZ6e2cY8",
                      "amount": "3400.25",
                      "occurred_at": "2026-10-08T14:21:09Z",
                      "bucket": "gross"
                    },
                    {
                      "id": "3Kwx2xzxmJwbFWTphgpnTTgwcS8",
                      "batch_id": "3KBsl1R1SZjAG7lN66md59RgpOh",
                      "ref": "entry:3KWAICWHTUyv0N9xGLHm286dPvJ",
                      "kind": "fee",
                      "entry_id": "3KWAICWHTUyv0N9xGLHm286dPvJ",
                      "transaction_id": "3KjLY7BV6lTzyJvGXr3mZ6e2cY8",
                      "amount": "-85.01",
                      "occurred_at": "2026-10-08T14:21:09Z",
                      "bucket": "fees"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such settlement.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such settlement",
                  "error": {
                    "code": "not_found",
                    "message": "no such settlement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/settlements/{id}/report": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "settlementReport",
        "summary": "Settlement report (CSV or PDF)",
        "description": "The merchant's settlement report: the totals and every line. Requires `settlements:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "pdf"
              ],
              "default": "csv"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/csv or application/pdf.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "The settlement report as CSV."
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The settlement report as PDF."
                }
              }
            }
          },
          "404": {
            "description": "No such settlement.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such settlement",
                  "error": {
                    "code": "not_found",
                    "message": "no such settlement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/settlements/run": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "runSettlements",
        "summary": "Build the settlement batches due now",
        "description": "Runs the hourly settlement job at once. Requires `admin:write`.",
        "responses": {
          "200": {
            "description": "`{built, paid}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "built": {
                      "type": "integer"
                    },
                    "paid": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "built": 3,
                  "paid": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/settlement": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getSettlementSettings",
        "summary": "A merchant's settlement cycle and destination",
        "description": "The cycle (T+n days), the method and the destination (masked), and a pending change. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{account_id, cycle_days, method, destination, beneficiary_name, pending}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "cycle_days": {
                      "type": "integer"
                    },
                    "method": {
                      "type": "string"
                    },
                    "destination": {
                      "type": "string"
                    },
                    "pending": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "beneficiary_name": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "cycle_days": 1,
                  "method": "mpesa",
                  "destination": "********4567",
                  "pending": {
                    "id": "3KMuWRw3JaLEf2SolKp9TuBxIGy",
                    "kind": "settlement.destination",
                    "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "reason": "O comerciante pediu liquida\u00e7\u00e3o a cada 2 dias",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-10T15:05:44Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such account",
                  "error": {
                    "code": "not_found",
                    "message": "no such account"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Ledger"
        ],
        "operationId": "putSettlementSettings",
        "summary": "Request a settlement change",
        "description": "Where a merchant's money is paid is a fraud target, so a change of the cycle or destination waits for a second person (`POST /v1/admin/approvals/{id}/decide`). `method` is a wallet (mpesa, emola: `destination` is the MSISDN) or `bank`: `destination` is the account's NIB (21 digits, check digits verified; spaces are ignored) and `beneficiary_name` its holder, both required. A bank destination is paid by the bank rail: the payout goes in the next batch to the sponsor bank, the settlement batch stays `paying` until the bank debits it, then `paid`, with `bank_reference` (the EndToEndId on the bank's statement). The per-beneficiary daily limit of the rail does not apply to settlements. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cycle_days",
                  "reason"
                ],
                "properties": {
                  "cycle_days": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 30
                  },
                  "method": {
                    "type": "string"
                  },
                  "destination": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  },
                  "beneficiary_name": {
                    "type": "string",
                    "description": "The bank account holder; required with method `bank`."
                  }
                }
              },
              "example": {
                "cycle_days": 1,
                "method": "bank",
                "destination": "000300010000001234585",
                "beneficiary_name": "Loja Ana, Lda",
                "reason": "the merchant's bank letter of 2026-10-10"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KMuWRw3JaLEf2SolKp9TuBxIGy",
                    "kind": "settlement.destination",
                    "subject_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "payload": "{\"cycle_days\":2,\"method\":\"mpesa\",\"destination\":\"+258841234567\"}",
                    "reason": "O comerciante pediu liquida\u00e7\u00e3o a cada 2 dias",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-10T15:05:44Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid settings, or a change already pending.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "cycle_days must be 0 to 30",
                  "error": {
                    "code": "invalid_request",
                    "message": "cycle_days must be 0 to 30"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such account",
                  "error": {
                    "code": "not_found",
                    "message": "no such account"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/statement-profiles": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getStatementProfiles",
        "summary": "Statement profiles",
        "description": "The PSP's active statement profiles and their version (PSP-11.1). Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{version, profiles}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "profiles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "standard": {
                            "type": "string"
                          },
                          "direction": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string"
                          },
                          "frequency": {
                            "type": "string"
                          },
                          "cutoff": {
                            "type": "string"
                          },
                          "timezone": {
                            "type": "string"
                          },
                          "arrival_window": {
                            "type": "string"
                          },
                          "granularity": {
                            "type": "string"
                          },
                          "security": {
                            "type": "object",
                            "properties": {
                              "ssh_key": {
                                "type": "boolean"
                              },
                              "mtls": {
                                "type": "boolean"
                              },
                              "pgp": {
                                "type": "boolean"
                              },
                              "checksum": {
                                "type": "string"
                              }
                            }
                          },
                          "sequence_numbering": {
                            "type": "boolean"
                          },
                          "acknowledgement": {
                            "type": "string"
                          },
                          "accounts": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "sample_file_id": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "version": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "profiles": [
                    {
                      "id": "eod",
                      "standard": "camt.053",
                      "direction": "push",
                      "channel": "api_upload",
                      "frequency": "daily",
                      "cutoff": "23:00",
                      "timezone": "Africa/Maputo",
                      "arrival_window": "2h",
                      "granularity": "transaction",
                      "security": {
                        "ssh_key": false,
                        "mtls": false,
                        "pgp": false,
                        "checksum": "sha256"
                      },
                      "sequence_numbering": false,
                      "acknowledgement": "api",
                      "accounts": [
                        "MZ59000800000000123456789"
                      ],
                      "sample_file_id": "3KWPr5AUvVJpugmIt8dBVHit0KE"
                    }
                  ],
                  "version": 1
                }
              }
            }
          },
          "404": {
            "description": "Not your PSP.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "PSP not found",
                  "error": {
                    "code": "not_found",
                    "message": "PSP not found"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "putStatementProfiles",
        "summary": "Declare statement profiles",
        "description": "A new version of the PSP's statement profiles (PSP-11.1): each statement it produces, with its standard, delivery (direction and channel), frequency and cut-off in its time zone, arrival window, granularity, security, file-name pattern, encoding, decimal and date formats, sequence numbering, field mapping, acknowledgement and the accounts it covers. A PSP may declare several (e.g. an intraday API feed and an end-of-day file). Each profile cites a sample from the preview that parsed; without one it cannot be activated. Versioned and audited (`config.statement_profile.version_created`, `config.statement_profile.activated`). Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "profiles",
                  "reason"
                ],
                "properties": {
                  "profiles": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "id",
                        "standard",
                        "direction",
                        "channel",
                        "frequency",
                        "cutoff",
                        "timezone",
                        "arrival_window",
                        "granularity",
                        "accounts"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "standard": {
                          "type": "string",
                          "description": "camt.052, camt.053, camt.054, MT940, MT942, BAI2, CSV, XLSX, JSON or a named plugin."
                        },
                        "direction": {
                          "type": "string",
                          "enum": [
                            "push",
                            "pull"
                          ]
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "sftp_kulpay",
                            "sftp_psp",
                            "api_upload",
                            "api_polling",
                            "webhook"
                          ]
                        },
                        "frequency": {
                          "type": "string",
                          "enum": [
                            "intraday",
                            "hourly",
                            "daily",
                            "weekly",
                            "monthly"
                          ]
                        },
                        "cutoff": {
                          "type": "string",
                          "example": "23:00"
                        },
                        "timezone": {
                          "type": "string",
                          "example": "Africa/Maputo"
                        },
                        "arrival_window": {
                          "type": "string",
                          "example": "2h"
                        },
                        "granularity": {
                          "type": "string",
                          "enum": [
                            "transaction",
                            "batch",
                            "summary"
                          ]
                        },
                        "security": {
                          "type": "object",
                          "properties": {
                            "ssh_key": {
                              "type": "boolean"
                            },
                            "mtls": {
                              "type": "boolean"
                            },
                            "pgp": {
                              "type": "boolean"
                            },
                            "checksum": {
                              "type": "string",
                              "enum": [
                                "sha256",
                                "none"
                              ]
                            }
                          }
                        },
                        "file_name_pattern": {
                          "type": "string"
                        },
                        "encoding": {
                          "type": "string",
                          "enum": [
                            "utf-8",
                            "latin-1"
                          ]
                        },
                        "decimal_separator": {
                          "type": "string",
                          "enum": [
                            ".",
                            ","
                          ]
                        },
                        "date_format": {
                          "type": "string"
                        },
                        "delimiter": {
                          "type": "string"
                        },
                        "sheet": {
                          "type": "integer"
                        },
                        "sequence_numbering": {
                          "type": "boolean"
                        },
                        "mapping": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          },
                          "description": "CSV, XLSX: canonical field to column header; JSON: to path (\"lines\" is the array path)."
                        },
                        "acknowledgement": {
                          "type": "string",
                          "enum": [
                            "none",
                            "file",
                            "api"
                          ]
                        },
                        "currency": {
                          "type": "string"
                        },
                        "accounts": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "sample_file_id": {
                          "type": "string",
                          "description": "From the preview; required before the profiles can be activated."
                        }
                      }
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "profiles": [
                  {
                    "id": "eod",
                    "standard": "camt.053",
                    "direction": "push",
                    "channel": "api_upload",
                    "frequency": "daily",
                    "cutoff": "23:00",
                    "timezone": "Africa/Maputo",
                    "arrival_window": "2h",
                    "granularity": "transaction",
                    "security": {
                      "checksum": "sha256"
                    },
                    "acknowledgement": "api",
                    "sample_file_id": "3KWPr5AUvVJpugmIt8dBVHit0KE",
                    "accounts": [
                      "MZ59000800000000123456789"
                    ]
                  }
                ],
                "reason": "end-of-day camt.053"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {},
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "activated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": null,
                  "version": {
                    "id": "3KWPrAnaCx5mmnq8rkGpK4AocSM",
                    "psp_id": "3KWPr8ZKH8CL3inFQ5xFPoFVIct",
                    "kind": "statement_profile",
                    "version": 1,
                    "status": "active",
                    "reason": "end-of-day camt.053",
                    "created_by": "3KWPrAT32mXDTPScHvhjyVEWawA",
                    "created_at": "2026-10-10T22:15:46.909605284Z",
                    "activated_at": "2026-10-10T22:15:46.911188374Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A profile is invalid or its sample does not parse; the problems are named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "statement profiles: profiles[0]: upload a sample first (POST /v1/psps/{id}/statement-profiles/preview); it must parse before activation; profiles[1]: the sample does not parse: line 4: amount \"1.250,00\" is not a decimal",
                  "error": {
                    "code": "invalid_request",
                    "message": "statement profiles: profiles[0]: upload a sample first (POST /v1/psps/{id}/statement-profiles/preview); it must parse before activation; profiles[1]: the sample does not parse: line 4: amount \"1.250,00\" is not a decimal"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/statement-profiles/preview": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "previewStatementProfile",
        "summary": "Preview a sample statement",
        "description": "Parses a sample file with a profile and returns the canonical lines (line ID, dates, amount in minor units, direction, references, counterparty, depositor, fee, raw line) before the profile is saved. A sample that parses is kept and its `sample_file_id` goes in the profile. JSON `{profile, sample (base64), filename}` or multipart (`profile` field, `sample` file). Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "profile",
                  "sample"
                ],
                "properties": {
                  "profile": {
                    "type": "object",
                    "required": [
                      "id",
                      "standard",
                      "direction",
                      "channel",
                      "frequency",
                      "cutoff",
                      "timezone",
                      "arrival_window",
                      "granularity",
                      "accounts"
                    ],
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "standard": {
                        "type": "string",
                        "description": "camt.052, camt.053, camt.054, MT940, MT942, BAI2, CSV, XLSX, JSON or a named plugin."
                      },
                      "direction": {
                        "type": "string",
                        "enum": [
                          "push",
                          "pull"
                        ]
                      },
                      "channel": {
                        "type": "string",
                        "enum": [
                          "sftp_kulpay",
                          "sftp_psp",
                          "api_upload",
                          "api_polling",
                          "webhook"
                        ]
                      },
                      "frequency": {
                        "type": "string",
                        "enum": [
                          "intraday",
                          "hourly",
                          "daily",
                          "weekly",
                          "monthly"
                        ]
                      },
                      "cutoff": {
                        "type": "string",
                        "example": "23:00"
                      },
                      "timezone": {
                        "type": "string",
                        "example": "Africa/Maputo"
                      },
                      "arrival_window": {
                        "type": "string",
                        "example": "2h"
                      },
                      "granularity": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "batch",
                          "summary"
                        ]
                      },
                      "security": {
                        "type": "object",
                        "properties": {
                          "ssh_key": {
                            "type": "boolean"
                          },
                          "mtls": {
                            "type": "boolean"
                          },
                          "pgp": {
                            "type": "boolean"
                          },
                          "checksum": {
                            "type": "string",
                            "enum": [
                              "sha256",
                              "none"
                            ]
                          }
                        }
                      },
                      "file_name_pattern": {
                        "type": "string"
                      },
                      "encoding": {
                        "type": "string",
                        "enum": [
                          "utf-8",
                          "latin-1"
                        ]
                      },
                      "decimal_separator": {
                        "type": "string",
                        "enum": [
                          ".",
                          ","
                        ]
                      },
                      "date_format": {
                        "type": "string"
                      },
                      "delimiter": {
                        "type": "string"
                      },
                      "sheet": {
                        "type": "integer"
                      },
                      "sequence_numbering": {
                        "type": "boolean"
                      },
                      "mapping": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        },
                        "description": "CSV, XLSX: canonical field to column header; JSON: to path (\"lines\" is the array path)."
                      },
                      "acknowledgement": {
                        "type": "string",
                        "enum": [
                          "none",
                          "file",
                          "api"
                        ]
                      },
                      "currency": {
                        "type": "string"
                      },
                      "accounts": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "sample_file_id": {
                        "type": "string",
                        "description": "From the preview; required before the profiles can be activated."
                      }
                    }
                  },
                  "sample": {
                    "type": "string",
                    "format": "byte"
                  },
                  "filename": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "profile": {
                  "id": "eod",
                  "standard": "camt.053",
                  "direction": "push",
                  "channel": "api_upload",
                  "frequency": "daily",
                  "cutoff": "23:00",
                  "timezone": "Africa/Maputo",
                  "arrival_window": "2h",
                  "granularity": "transaction",
                  "security": {
                    "checksum": "none"
                  },
                  "acknowledgement": "none",
                  "accounts": [
                    "MZ59000800000000123456789"
                  ]
                },
                "sample": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPERvY3VtZW50IHhtbG5zPSJ1cm46aXNvOnN0ZDppc286MjAwMjI6dGVjaDp4c2Q6Y2Ft\u2026"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "profile": {
                    "type": "string"
                  },
                  "sample": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{sample_file_id, statement}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sample_file_id": {
                      "type": "string"
                    },
                    "statement": {
                      "type": "object",
                      "properties": {
                        "standard": {
                          "type": "string"
                        },
                        "statement_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "sequence": {
                          "type": "string"
                        },
                        "has_balances": {
                          "type": "boolean"
                        },
                        "opening_minor": {
                          "type": "integer"
                        },
                        "closing_minor": {
                          "type": "integer"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "line_id": {
                                "type": "string"
                              },
                              "statement_id": {
                                "type": "string"
                              },
                              "account": {
                                "type": "string"
                              },
                              "value_date": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "booking_date": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "amount_minor": {
                                "type": "integer"
                              },
                              "currency": {
                                "type": "string"
                              },
                              "direction": {
                                "type": "string"
                              },
                              "psp_reference": {
                                "type": "string"
                              },
                              "kulpay_reference": {
                                "type": "string"
                              },
                              "end_to_end_id": {
                                "type": "string"
                              },
                              "counterparty": {
                                "type": "string"
                              },
                              "depositor_ref": {
                                "type": "string"
                              },
                              "line_no": {
                                "type": "integer"
                              },
                              "raw": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "credits_minor": {
                          "type": "integer"
                        },
                        "debits_minor": {
                          "type": "integer"
                        },
                        "checked_balance": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "sample_file_id": "3KWPr5AUvVJpugmIt8dBVHit0KE",
                  "statement": {
                    "standard": "camt.053",
                    "statement_id": "BCI-20261008-0001",
                    "account": "MZ59000800000000123456789",
                    "currency": "MZN",
                    "sequence": "281",
                    "has_balances": true,
                    "opening_minor": 1000000,
                    "closing_minor": 1425050,
                    "created_at": "2026-10-08T23:10:00Z",
                    "lines": [
                      {
                        "line_id": "E-0001",
                        "statement_id": "BCI-20261008-0001",
                        "account": "MZ59000800000000123456789",
                        "value_date": "2026-10-08T00:00:00Z",
                        "booking_date": "2026-10-08T00:00:00Z",
                        "amount_minor": 500050,
                        "currency": "MZN",
                        "direction": "credit",
                        "psp_reference": "BCI8812001",
                        "kulpay_reference": "3KT1BI90QLq2PDUscP8QqzCEW4f",
                        "end_to_end_id": "3KT1BI90QLq2PDUscP8QqzCEW4f",
                        "counterparty": "Loja Exemplo Lda",
                        "depositor_ref": "KP-ACC-1001",
                        "line_no": 1,
                        "raw": "<NtryRef>E-0001</NtryRef> <Amt Ccy=\"MZN\">5000.50</Amt> <CdtDbtInd>CRDT</CdtDbtInd> <Sts>BOOK</Sts> <BookgDt><Dt>2026-10-\u2026"
                      },
                      {
                        "line_id": "E-0002",
                        "statement_id": "BCI-20261008-0001",
                        "account": "MZ59000800000000123456789",
                        "value_date": "2026-10-08T00:00:00Z",
                        "booking_date": "2026-10-08T00:00:00Z",
                        "amount_minor": 75000,
                        "currency": "MZN",
                        "direction": "debit",
                        "psp_reference": "BCI8812002",
                        "counterparty": "Fornecedor SA",
                        "fee_minor": 1500,
                        "line_no": 2,
                        "raw": "<NtryRef>E-0002</NtryRef> <Amt Ccy=\"MZN\">750.00</Amt> <CdtDbtInd>DBIT</CdtDbtInd> <Sts>BOOK</Sts> <BookgDt><Dt>2026-10-0\u2026"
                      }
                    ],
                    "credits_minor": 500050,
                    "debits_minor": 75000,
                    "checked_balance": true
                  }
                }
              }
            }
          },
          "422": {
            "description": "The profile is invalid or the sample does not parse (`sample_rejected`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "sample_rejected",
                  "detail": "the sample does not parse: camt.053: XML syntax error on line 1: unexpected EOF",
                  "error": {
                    "code": "sample_rejected",
                    "message": "the sample does not parse: camt.053: XML syntax error on line 1: unexpected EOF"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "uploadStatement",
        "summary": "Send a statement",
        "description": "A PSP pushes a statement under one of its statement profiles (PSP-11.2): multipart with `file`, `profile_id` and `checksum` (hex SHA-256; or an RFC 9530 `Content-Digest: sha-256=:...:` header), required where the profile says so. Where the profile uses PGP, the file must be encrypted to KulPay's region key and signed with one of the PSP's registered PGP keys. Events: recon.file.received, then recon.file.ingested, recon.file.duplicate or recon.file.rejected. Requires `reconciliation:write` (`recon:write`).",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "profile_id"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "profile_id": {
                    "type": "string"
                  },
                  "checksum": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received before (same SHA-256, or the same sequence number for the account): `{file, duplicate: true, reason}`; stop resending.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "profile_id": {
                          "type": "string"
                        },
                        "profile_version": {
                          "type": "integer"
                        },
                        "standard": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "file_id": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "statement_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "sequence": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "parse_version": {
                          "type": "integer"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "skipped_lines": {
                          "type": "integer"
                        },
                        "received_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "duplicate": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "file": {
                    "id": "3KWPrOa2OznFbpDMmWuxdPEcGGI",
                    "psp_id": "3KWPrLbezXsS8uP4YCxyxiQdz0U",
                    "tenant_id": "3KWPrRZnp2PDBLR40icYFsmgfL0",
                    "profile_id": "eod",
                    "profile_version": 1,
                    "standard": "camt.053",
                    "channel": "api_upload",
                    "file_id": "3KWPrPPCxR2tKQxfsC1oq4MqomH",
                    "filename": "BCI_20261008.xml",
                    "sha256": "9efc248a730f0c5e4dc9f55c3e5df8ea8f94a2ecd8266d5bb2e7a7adb3ea0afc",
                    "size": 2116,
                    "statement_id": "BCI-20261008-0001",
                    "account": "MZ59000800000000123456789",
                    "sequence": "281",
                    "status": "ingested",
                    "parse_version": 1,
                    "line_count": 2,
                    "skipped_lines": 0,
                    "received_at": "2026-10-10T22:15:48.852204595Z",
                    "updated_at": "2026-10-10T22:15:48.852738905Z"
                  },
                  "duplicate": true,
                  "reason": "the same file (checksum) was received before"
                }
              }
            }
          },
          "201": {
            "description": "Ingested: `{file}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "profile_id": {
                          "type": "string"
                        },
                        "profile_version": {
                          "type": "integer"
                        },
                        "standard": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "file_id": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "statement_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "sequence": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "parse_version": {
                          "type": "integer"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "skipped_lines": {
                          "type": "integer"
                        },
                        "received_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "file": {
                    "id": "3KWPrOa2OznFbpDMmWuxdPEcGGI",
                    "psp_id": "3KWPrLbezXsS8uP4YCxyxiQdz0U",
                    "tenant_id": "3KWPrRZnp2PDBLR40icYFsmgfL0",
                    "profile_id": "eod",
                    "profile_version": 1,
                    "standard": "camt.053",
                    "channel": "api_upload",
                    "file_id": "3KWPrPPCxR2tKQxfsC1oq4MqomH",
                    "filename": "BCI_20261008.xml",
                    "sha256": "9efc248a730f0c5e4dc9f55c3e5df8ea8f94a2ecd8266d5bb2e7a7adb3ea0afc",
                    "size": 2116,
                    "statement_id": "BCI-20261008-0001",
                    "account": "MZ59000800000000123456789",
                    "sequence": "281",
                    "status": "ingested",
                    "parse_version": 1,
                    "line_count": 2,
                    "skipped_lines": 0,
                    "received_at": "2026-10-10T22:15:48.852204595Z",
                    "updated_at": "2026-10-10T22:15:48.852738905Z"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Rejected (`statement_rejected`) with the reason: checksum, PGP, parse, balances or account. The file is kept for investigation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "statement_rejected",
                  "detail": "the SHA-256 checksum does not match the file",
                  "error": {
                    "code": "statement_rejected",
                    "message": "the SHA-256 checksum does not match the file"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listStatementFiles",
        "summary": "Received statements",
        "description": "The statement files the account's PSPs sent, newest first, accepted (`ingested`) or `rejected` with the reason (PSP-11.5). Each keeps its SHA-256, the profile and version it arrived under, the parse in force, and its line counts. Requires `reconciliation:read` (`recon:read`).",
        "responses": {
          "200": {
            "description": "`{data: [file], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "profile_id": {
                            "type": "string"
                          },
                          "profile_version": {
                            "type": "integer"
                          },
                          "standard": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string"
                          },
                          "file_id": {
                            "type": "string"
                          },
                          "filename": {
                            "type": "string"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "statement_id": {
                            "type": "string"
                          },
                          "account": {
                            "type": "string"
                          },
                          "sequence": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "parse_version": {
                            "type": "integer"
                          },
                          "line_count": {
                            "type": "integer"
                          },
                          "skipped_lines": {
                            "type": "integer"
                          },
                          "received_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPrIwQUxSZ2AOwicNJEuBUepa",
                      "psp_id": "3KWPrH3E7T3C55pLkkE0rfJoJIV",
                      "tenant_id": "3KWPrHb2epuElvaBAOfxT5ZBJeg",
                      "profile_id": "eod",
                      "profile_version": 1,
                      "standard": "camt.053",
                      "channel": "api_upload",
                      "file_id": "3KWPrJNcwI1wKV92rMyvutErg7p",
                      "filename": "BCI.xml",
                      "sha256": "9efc248a730f0c5e4dc9f55c3e5df8ea8f94a2ecd8266d5bb2e7a7adb3ea0afc",
                      "size": 2116,
                      "statement_id": "BCI-20261008-0001",
                      "account": "MZ59000800000000123456789",
                      "sequence": "281",
                      "status": "ingested",
                      "parse_version": 1,
                      "line_count": 2,
                      "skipped_lines": 0,
                      "received_at": "2026-10-10T22:15:47.810480573Z",
                      "updated_at": "2026-10-10T22:15:47.811510034Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "getStatementFile",
        "summary": "A received statement and its parse versions",
        "description": "Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file with `parses`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "profile_id": {
                      "type": "string"
                    },
                    "profile_version": {
                      "type": "integer"
                    },
                    "standard": {
                      "type": "string"
                    },
                    "channel": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "size": {
                      "type": "integer"
                    },
                    "statement_id": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "sequence": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "parse_version": {
                      "type": "integer"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "skipped_lines": {
                      "type": "integer"
                    },
                    "received_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "parses": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "statement_file_id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "profile_version": {
                            "type": "integer"
                          },
                          "standard": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "line_count": {
                            "type": "integer"
                          },
                          "credits_minor": {
                            "type": "integer"
                          },
                          "debits_minor": {
                            "type": "integer"
                          },
                          "has_balances": {
                            "type": "boolean"
                          },
                          "opening_minor": {
                            "type": "integer"
                          },
                          "closing_minor": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KUXZ4vLe1lXo7yApRrEi2GcTm2",
                  "psp_id": "3KUYA7wMf4mYp1zBqSsFj5HdUn3",
                  "tenant_id": "3KUYB1xNg7nZq4ACrTtGk8IeVo4",
                  "profile_id": "eod",
                  "profile_version": 2,
                  "standard": "camt.053",
                  "channel": "api_upload",
                  "file_id": "3KUYC4yOh1oAr7BDsUuHl2JfWp5",
                  "filename": "camt053_20261010.xml",
                  "sha256": "9f2c4e7a1b3d5f60718293a4b5c6d7e8f9012a3b4c5d6e7f8091a2b3c4d5e6f7",
                  "size": 48213,
                  "statement_id": "STMT-20261010-001",
                  "account": "000100000012345678957",
                  "sequence": "283",
                  "status": "ingested",
                  "parse_version": 2,
                  "line_count": 42,
                  "skipped_lines": 0,
                  "received_at": "2026-10-10T23:05:12Z",
                  "updated_at": "2026-10-11T08:40:02Z",
                  "parses": [
                    {
                      "id": "3KUYD7zPi4pBs1CEtVvIm5KgXq6",
                      "statement_file_id": "3KUXZ4vLe1lXo7yApRrEi2GcTm2",
                      "version": 1,
                      "profile_version": 1,
                      "standard": "camt.053",
                      "currency": "MZN",
                      "line_count": 42,
                      "credits_minor": 18450075,
                      "debits_minor": 2310000,
                      "has_balances": true,
                      "opening_minor": 120000000,
                      "closing_minor": 136140075,
                      "created_at": "2026-10-10T23:05:13Z"
                    },
                    {
                      "id": "3KUYE1APj7qCt4DFuWwJn8LhYr7",
                      "statement_file_id": "3KUXZ4vLe1lXo7yApRrEi2GcTm2",
                      "version": 2,
                      "profile_version": 2,
                      "standard": "camt.053",
                      "reason": "Mapping fix: end-to-end id read from the right element",
                      "currency": "MZN",
                      "line_count": 42,
                      "credits_minor": 18450075,
                      "debits_minor": 2310000,
                      "has_balances": true,
                      "opening_minor": 120000000,
                      "closing_minor": 136140075,
                      "created_at": "2026-10-11T08:40:02Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such statement.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such statement",
                  "error": {
                    "code": "not_found",
                    "message": "no such statement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}/lines": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "statementFileLines",
        "summary": "Canonical lines of a statement",
        "description": "The canonical lines of a parse version (the one in force by default), each linked to the file and its line number; never edited. A line ID already imported for the account from another file is not imported again. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "parse_version",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 1000
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [line], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "account": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "booking_date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "counterparty": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "depositor_ref": {
                            "type": "string"
                          },
                          "direction": {
                            "type": "string"
                          },
                          "end_to_end_id": {
                            "type": "string"
                          },
                          "fee_minor": {
                            "type": "integer"
                          },
                          "id": {
                            "type": "string"
                          },
                          "kulpay_reference": {
                            "type": "string"
                          },
                          "line_id": {
                            "type": "string"
                          },
                          "line_no": {
                            "type": "integer"
                          },
                          "parse_id": {
                            "type": "string"
                          },
                          "parse_version": {
                            "type": "integer"
                          },
                          "psp_reference": {
                            "type": "string"
                          },
                          "raw": {
                            "type": "string"
                          },
                          "statement_file_id": {
                            "type": "string"
                          },
                          "statement_id": {
                            "type": "string"
                          },
                          "value_date": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "account": "****6789",
                      "amount_minor": 500050,
                      "booking_date": "2026-10-08T00:00:00Z",
                      "counterparty": "Loja Exemplo Lda",
                      "created_at": "2026-10-10T22:15:47.811510034Z",
                      "currency": "MZN",
                      "depositor_ref": "KP-ACC-1001",
                      "direction": "credit",
                      "end_to_end_id": "3KT1BI90QLq2PDUscP8QqzCEW4f",
                      "fee_minor": 0,
                      "id": "3KWPrIa67BOidWmhTHuGPKl1Gfh",
                      "kulpay_reference": "3KT1BI90QLq2PDUscP8QqzCEW4f",
                      "line_id": "E-0001",
                      "line_no": 1,
                      "parse_id": "3KWPrGCPt4BUOytmIYvf1SegEDX",
                      "parse_version": 1,
                      "psp_reference": "BCI8812001",
                      "raw": "<NtryRef>E-0001</NtryRef> <Amt Ccy=\"MZN\">5000.50</Amt> <CdtDbtInd>CRDT</CdtDbtInd> <Sts>BOOK</Sts> <BookgDt><Dt>2026-10-\u2026",
                      "statement_file_id": "3KWPrIwQUxSZ2AOwicNJEuBUepa",
                      "statement_id": "BCI-20261008-0001",
                      "value_date": "2026-10-08T00:00:00Z"
                    },
                    {
                      "account": "****6789",
                      "amount_minor": 75000,
                      "booking_date": "2026-10-08T00:00:00Z",
                      "counterparty": "Fornecedor SA",
                      "created_at": "2026-10-10T22:15:47.811510034Z",
                      "currency": "MZN",
                      "direction": "debit",
                      "fee_minor": 1500,
                      "id": "3KWPrGDSUvp2L3SyMYZqjxSPjzF",
                      "line_id": "E-0002",
                      "line_no": 2,
                      "parse_id": "3KWPrGCPt4BUOytmIYvf1SegEDX",
                      "parse_version": 1,
                      "psp_reference": "BCI8812002",
                      "raw": "<NtryRef>E-0002</NtryRef> <Amt Ccy=\"MZN\">750.00</Amt> <CdtDbtInd>DBIT</CdtDbtInd> <Sts>BOOK</Sts> <BookgDt><Dt>2026-10-0\u2026",
                      "statement_file_id": "3KWPrIwQUxSZ2AOwicNJEuBUepa",
                      "statement_id": "BCI-20261008-0001",
                      "value_date": "2026-10-08T00:00:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          },
          "404": {
            "description": "No such statement or parse version.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such statement",
                  "error": {
                    "code": "not_found",
                    "message": "no such statement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}/original": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "statementOriginal",
        "summary": "The original file",
        "description": "The file exactly as received, from the region's object storage, with `X-Content-SHA256`. Originals are write-once. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The statement file exactly as it was received."
                }
              }
            }
          },
          "404": {
            "description": "No such statement.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such statement",
                  "error": {
                    "code": "not_found",
                    "message": "no such statement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}/reparse": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "reparseStatementFile",
        "summary": "Re-parse a statement",
        "description": "Parses the stored original again with the profile now in force (after a mapping fix): a new parse version, the earlier ones kept. Records `recon.file.reparsed`. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "mapping fix"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The file with its new parse version.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "profile_id": {
                      "type": "string"
                    },
                    "profile_version": {
                      "type": "integer"
                    },
                    "standard": {
                      "type": "string"
                    },
                    "channel": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "size": {
                      "type": "integer"
                    },
                    "statement_id": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "sequence": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "parse_version": {
                      "type": "integer"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "skipped_lines": {
                      "type": "integer"
                    },
                    "received_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPrIwQUxSZ2AOwicNJEuBUepa",
                  "psp_id": "3KWPrH3E7T3C55pLkkE0rfJoJIV",
                  "tenant_id": "3KWPrHb2epuElvaBAOfxT5ZBJeg",
                  "profile_id": "eod",
                  "profile_version": 1,
                  "standard": "camt.053",
                  "channel": "api_upload",
                  "file_id": "3KWPrJNcwI1wKV92rMyvutErg7p",
                  "filename": "BCI.xml",
                  "sha256": "9efc248a730f0c5e4dc9f55c3e5df8ea8f94a2ecd8266d5bb2e7a7adb3ea0afc",
                  "size": 2116,
                  "statement_id": "BCI-20261008-0001",
                  "account": "MZ59000800000000123456789",
                  "sequence": "281",
                  "status": "ingested",
                  "parse_version": 2,
                  "line_count": 2,
                  "skipped_lines": 0,
                  "received_at": "2026-10-10T22:15:47.810480573Z",
                  "updated_at": "2026-10-10T22:15:48.012072427Z"
                }
              }
            }
          },
          "400": {
            "description": "No reason, or it does not parse with the current profile.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a reason is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a reason is required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/statement-files": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "adminListStatementFiles",
        "summary": "Every PSP's received statements",
        "description": "For KulPay operators: every PSP's files. Also `/v1/admin/statement-files/{id}`, `/lines` and `/reparse`. Requires `admin:read`.",
        "responses": {
          "200": {
            "description": "`{data: [file], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "profile_id": {
                            "type": "string"
                          },
                          "profile_version": {
                            "type": "integer"
                          },
                          "standard": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string"
                          },
                          "file_id": {
                            "type": "string"
                          },
                          "filename": {
                            "type": "string"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer"
                          },
                          "statement_id": {
                            "type": "string"
                          },
                          "account": {
                            "type": "string"
                          },
                          "sequence": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "parse_version": {
                            "type": "integer"
                          },
                          "line_count": {
                            "type": "integer"
                          },
                          "skipped_lines": {
                            "type": "integer"
                          },
                          "received_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KdhOrsZV8VzDwRsx2nNjQAi6kX",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "tenant_id": "3K12JLy9OnIwuN9yzoIgIKQpHJX",
                      "profile_id": "bci-camt053-daily",
                      "profile_version": 2,
                      "standard": "camt.053",
                      "channel": "sftp_kulpay",
                      "file_id": "3KJ5EjFon9jvDDSDRLTImADqQFz",
                      "filename": "BCI_CAMT053_20261011.xml",
                      "sha256": "127eb02825bf27f39d3330cb11c7f12aa587e25bf0582310d44c10c8163fb90b",
                      "size": 48213,
                      "statement_id": "STMT-20261011-0001",
                      "account": "000800001234567890157",
                      "sequence": "284",
                      "status": "ingested",
                      "parse_version": 1,
                      "line_count": 137,
                      "skipped_lines": 0,
                      "received_at": "2026-10-11T06:02:11Z",
                      "updated_at": "2026-10-11T06:02:13Z"
                    },
                    {
                      "id": "3KDLNgOU39cCXqtNJmVrtCTVqzG",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "tenant_id": "3K12JLy9OnIwuN9yzoIgIKQpHJX",
                      "profile_id": "bci-camt053-daily",
                      "profile_version": 2,
                      "standard": "camt.053",
                      "channel": "sftp_kulpay",
                      "file_id": "3KEKunzWl0OtBzYAL3qgpjqv49A",
                      "filename": "BCI_CAMT053_20261010_v2.xml",
                      "sha256": "e454a9599172d285c27e688ef7618a5a2e06295d8e30336299cb46a54391d5a7",
                      "size": 1024,
                      "status": "rejected",
                      "reason": "the file is not a camt.053 document: missing BkToCstmrStmt",
                      "parse_version": 0,
                      "line_count": 0,
                      "skipped_lines": 0,
                      "received_at": "2026-10-10T06:15:40Z",
                      "updated_at": "2026-10-10T06:15:40Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/keys": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "listPSPKeys",
        "summary": "Transport keys",
        "description": "The PSP's active SSH and PGP public keys. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [key], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string"
                          },
                          "public_key": {
                            "type": "string"
                          },
                          "fingerprint": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPrVqAItzPWymXFh1TU7sxJ8S",
                      "psp_id": "3KWPrLbezXsS8uP4YCxyxiQdz0U",
                      "kind": "ssh",
                      "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMHN9XlAWufAHy8BVF3iELDlHS3d9XVcW+KzkS89F0OU",
                      "fingerprint": "SHA256:FtlACmaRUkSpmcOioTf/fQ1zdkUFFp81k4cVSg3MHH4",
                      "created_by": "3KWPrQT3b9J2gFndeaFBcOpkzZk",
                      "created_at": "2026-10-10T22:15:49.010888336Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "registerPSPKey",
        "summary": "Register an SSH or PGP public key",
        "description": "`ssh`: an authorized_keys line (Ed25519, ECDSA or RSA), the only way into KulPay-hosted SFTP. `pgp`: an ASCII-armored public key verifying the PSP's statement signatures. Records `credential.psp_key.registered`. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "public_key"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "ssh",
                      "pgp"
                    ]
                  },
                  "public_key": {
                    "type": "string"
                  },
                  "comment": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "kind": "ssh",
                "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMHN9XlAWufAHy8BVF3iELDlHS3d9XVcW+KzkS89F0OU"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key with its fingerprint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "public_key": {
                      "type": "string"
                    },
                    "fingerprint": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPrVqAItzPWymXFh1TU7sxJ8S",
                  "psp_id": "3KWPrLbezXsS8uP4YCxyxiQdz0U",
                  "kind": "ssh",
                  "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMHN9XlAWufAHy8BVF3iELDlHS3d9XVcW+KzkS89F0OU",
                  "fingerprint": "SHA256:FtlACmaRUkSpmcOioTf/fQ1zdkUFFp81k4cVSg3MHH4",
                  "created_by": "3KWPrQT3b9J2gFndeaFBcOpkzZk",
                  "created_at": "2026-10-10T22:15:49.010888336Z"
                }
              }
            }
          },
          "400": {
            "description": "Not a valid key, or already registered.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "public_key is not an SSH public key (authorized_keys format)",
                  "error": {
                    "code": "invalid_request",
                    "message": "public_key is not an SSH public key (authorized_keys format)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/keys/{key_id}": {
      "delete": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "revokePSPKey",
        "summary": "Revoke a transport key",
        "description": "Stops working at once. Records `credential.psp_key.revoked`. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "key_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "404": {
            "description": "No such active key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such active key",
                  "error": {
                    "code": "not_found",
                    "message": "no such active key"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/statement-endpoints": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "statementEndpoints",
        "summary": "Where to send statements",
        "description": "KulPay-hosted SFTP (address, user = the PSP ID, host key and its SHA-256 fingerprint), KulPay's PGP public key for this region, the upload path and the per-entry webhook path. Over SFTP the PSP writes into its own write-only folder; a file is matched to its sftp_kulpay profile by name, and where a checksum is required a `<file>.sha256` sidecar carries it. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{sftp_address, sftp_user, sftp_host_key, sftp_host_key_fingerprint, pgp_public_key, pgp_fingerprint, upload_path, webhook_path}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sftp_user": {
                      "type": "string"
                    },
                    "sftp_host_key": {
                      "type": "string"
                    },
                    "sftp_host_key_fingerprint": {
                      "type": "string"
                    },
                    "pgp_public_key": {
                      "type": "string"
                    },
                    "pgp_fingerprint": {
                      "type": "string"
                    },
                    "upload_path": {
                      "type": "string"
                    },
                    "webhook_path": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "sftp_user": "3KWPrLbezXsS8uP4YCxyxiQdz0U",
                  "sftp_host_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJipMDgGetJsqh64QKWkI8QsHW9Fea1BPu7N/8tZ/QVl",
                  "sftp_host_key_fingerprint": "SHA256:U0tDE+26h2y7Nk42LWqWemUfppilRPFYOv1nKlKE6tM",
                  "pgp_public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxsDNBGrKuRUBDACshel2oJCvIWi4VIi5ddjpl300qL+ZtSSP+EEX6yASuNCMetA1\naCjNYvGWoGfjokgcW\u2026",
                  "pgp_fingerprint": "E2D9CD60195D1C56D1A202291C52F0688B5AD15D",
                  "upload_path": "/v1/statements",
                  "webhook_path": "/callbacks/3KWPrLbezXsS8uP4YCxyxiQdz0U/{binding_id}/statements/{profile_id}"
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/commissions": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getOwnCommissionStatement",
        "summary": "My commissions as a participant",
        "description": "What KulPay owes the PSP for its part in others' payments in a month (IO-4.4): each succeeded bill payment it took part in (initiating_psp, from_psp, to_psp) whose role a rule of its fee schedule prices (rule `role`), the amount posted to its balance with the payment as the reference, and the totals per currency. `month` is YYYY-MM (this month by default). Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The statement.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "month": {
                      "type": "string",
                      "example": "2026-10"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "example": {
                        "MZN": "1250.00"
                      }
                    },
                    "commissions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "payment_kind": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string",
                            "enum": [
                              "initiating_psp",
                              "from_psp",
                              "to_psp"
                            ]
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "base_minor": {
                            "type": "integer"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "schedule_id": {
                            "type": "string"
                          },
                          "schedule_version": {
                            "type": "integer"
                          },
                          "rule": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "psp_id": "3KXgPSPkulOffice00000000001",
                  "month": "2026-10",
                  "mode": "live",
                  "totals": {
                    "MZN": "12.50"
                  },
                  "commissions": [
                    {
                      "id": "3KXgCOM00000000000000000001",
                      "payment_id": "3KXgBILL0000000000000000001",
                      "payment_kind": "bill_payment",
                      "role": "initiating_psp",
                      "psp_id": "3KXgPSPkulOffice00000000001",
                      "account_id": "3KXgACCkulOffice0000000001",
                      "mode": "live",
                      "currency": "MZN",
                      "base_minor": 125000,
                      "amount_minor": 1250,
                      "schedule_id": "3KXgFEE00000000000000000001",
                      "schedule_version": 2,
                      "rule": 0,
                      "created_at": "2026-10-15T10:00:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/credit-report": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getOwnCreditReport",
        "summary": "My credits of a day",
        "description": "A resolver's credits applied on a day (IO-6.2), yesterday by default: deposit, credit, amount, status, when applied, the resolver's transaction and the settlement batch that paid it, with the totals per currency. `?format=csv` answers CSV. For the resolver itself, in the key's mode. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "date",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resolver_psp_id": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "totals": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    },
                    "credits": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "deposit_id": {
                            "type": "string"
                          },
                          "credit_id": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "applied_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "transaction_id": {
                            "type": "string"
                          },
                          "settlement_batch_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "resolver_psp_id": "3KXgPSPkulOffice00000000001",
                  "date": "2026-10-14",
                  "mode": "live",
                  "totals": {
                    "MZN": "500.00"
                  },
                  "credits": [
                    {
                      "deposit_id": "3KXgDEP00000000000000000001",
                      "credit_id": "3KXgCRD00000000000000000001",
                      "amount": "500.00",
                      "currency": "MZN",
                      "status": "applied",
                      "applied_at": "2026-10-14T09:12:00Z",
                      "transaction_id": "txn_blnk_1",
                      "settlement_batch_id": "3KXgBAT00000000000000000001"
                    }
                  ]
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/credit-reconciliations": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "postCreditReconciliation",
        "summary": "Report my reconciliation of a day",
        "description": "The resolver's result for a day (IO-6.2): its ledger and settled totals, the credits KulPay applied it cannot find (`missing_at_kuloffice`, credit IDs) and its ledger postings KulPay did not apply (`missing_at_kulpay`). The day closes when KulPay's applied total and both totals agree and nothing is missing; each difference is an exception with an owner (IO-6.3), and one open for 48 hours alerts both treasuries (`partner.alert`). Sent again, the day is recorded again; an exception is never raised twice. Requires `psp:configure`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "date",
                  "currency",
                  "ledger_total",
                  "settled_total"
                ],
                "properties": {
                  "date": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "ledger_total": {
                    "type": "string"
                  },
                  "settled_total": {
                    "type": "string"
                  },
                  "missing_at_kuloffice": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "missing_at_kulpay": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "reference": {
                          "type": "string"
                        },
                        "transaction_id": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "date": "2026-10-14",
                "currency": "MZN",
                "ledger_total": "500.00",
                "settled_total": "500.00",
                "missing_at_kuloffice": [],
                "missing_at_kulpay": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The day as recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "resolver_psp_id": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "applied_minor": {
                      "type": "integer"
                    },
                    "ledger_minor": {
                      "type": "integer"
                    },
                    "settled_minor": {
                      "type": "integer"
                    },
                    "missing_at_resolver": {
                      "type": "integer"
                    },
                    "missing_at_kulpay": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "closed",
                        "differences"
                      ]
                    },
                    "reported_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KXgREC00000000000000000001",
                  "resolver_psp_id": "3KXgPSPkulOffice00000000001",
                  "date": "2026-10-14",
                  "mode": "live",
                  "currency": "MZN",
                  "applied_minor": 50000,
                  "ledger_minor": 50000,
                  "settled_minor": 50000,
                  "missing_at_resolver": 0,
                  "missing_at_kulpay": 0,
                  "status": "closed",
                  "reported_at": "2026-10-15T02:00:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/callbacks/{psp_id}/{binding_id}/statements/{profile_id}": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "statementWebhook",
        "summary": "Per-entry statement webhook",
        "description": "A per-entry statement (e.g. a camt.054 notification) under a webhook profile, verified with the binding's own configuration like its callbacks (PSP-7.3): a failed check returns 401 and the file is kept as rejected. An optional RFC 9530 `Content-Digest` is checked.",
        "security": [],
        "parameters": [
          {
            "name": "psp_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "binding_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profile_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/xml": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Received before (same SHA-256, or the same sequence number for the account): `{file, duplicate: true, reason}`; stop resending.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "profile_id": {
                          "type": "string"
                        },
                        "profile_version": {
                          "type": "integer"
                        },
                        "standard": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "file_id": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "statement_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "sequence": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "parse_version": {
                          "type": "integer"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "skipped_lines": {
                          "type": "integer"
                        },
                        "received_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "duplicate": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "file": {
                    "id": "3KJ13tZUrt8HgsHTFEUWpgkcVqv",
                    "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                    "tenant_id": "3K01AFCO8zayIwIHSwO2tlZRWMR",
                    "profile_id": "notify",
                    "profile_version": 2,
                    "standard": "camt.054",
                    "channel": "webhook",
                    "file_id": "3K9iJ3uQ1qlVh1Y7bTtafJEpYIV",
                    "filename": "",
                    "sha256": "9f2c4e7a1b3d5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8",
                    "size": 4182,
                    "statement_id": "NTF-20261010-000731",
                    "account": "000100000012345678901",
                    "sequence": "731",
                    "status": "ingested",
                    "parse_version": 1,
                    "line_count": 3,
                    "skipped_lines": 0,
                    "received_at": "2026-10-10T11:04:52Z",
                    "updated_at": "2026-10-10T11:04:52Z"
                  },
                  "duplicate": true,
                  "reason": "the same file (checksum) was received before"
                }
              }
            }
          },
          "201": {
            "description": "Ingested: `{file}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "profile_id": {
                          "type": "string"
                        },
                        "profile_version": {
                          "type": "integer"
                        },
                        "standard": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "file_id": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "sha256": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "statement_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "sequence": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "parse_version": {
                          "type": "integer"
                        },
                        "line_count": {
                          "type": "integer"
                        },
                        "skipped_lines": {
                          "type": "integer"
                        },
                        "received_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "file": {
                    "id": "3KJ13tZUrt8HgsHTFEUWpgkcVqv",
                    "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                    "tenant_id": "3K01AFCO8zayIwIHSwO2tlZRWMR",
                    "profile_id": "notify",
                    "profile_version": 2,
                    "standard": "camt.054",
                    "channel": "webhook",
                    "file_id": "3K9iJ3uQ1qlVh1Y7bTtafJEpYIV",
                    "filename": "",
                    "sha256": "9f2c4e7a1b3d5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8",
                    "size": 4182,
                    "statement_id": "NTF-20261010-000731",
                    "account": "000100000012345678901",
                    "sequence": "731",
                    "status": "ingested",
                    "parse_version": 1,
                    "line_count": 3,
                    "skipped_lines": 0,
                    "received_at": "2026-10-10T11:04:52Z",
                    "updated_at": "2026-10-10T11:04:52Z"
                  }
                }
              }
            }
          },
          "401": {
            "description": "The binding's verification failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unauthorized",
                  "status": 401,
                  "code": "callback_rejected",
                  "detail": "the request did not pass this binding's verification",
                  "error": {
                    "code": "callback_rejected",
                    "message": "the request did not pass this binding's verification"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Rejected (`statement_rejected`) with the reason: checksum, PGP, parse, balances or account. The file is kept for investigation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "statement_rejected",
                  "detail": "the SHA-256 checksum does not match the file",
                  "error": {
                    "code": "statement_rejected",
                    "message": "the SHA-256 checksum does not match the file"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/cash-deposits": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "reportCashDeposit",
        "summary": "Confirm a cash deposit (banks)",
        "description": "A bank confirms a cash deposit (PSP-7.6): `entry_id` (its statement entry), `account` (the receiving account), `value_date`, `amount`, `currency` and `depositor_ref` (the depositor's KulPay account ID or tag), optionally `depositor_email`. Nothing is credited until a line of the receiving account's statement confirms it with the same entry ID, amount and currency; then the account is credited (`cash_in.confirmed`, a webhook to the account owner; the depositor is emailed). No valid account or tag: suspense (`cash_in.suspended`). No statement line within the statement profile's arrival window: `unconfirmed_deposit`; another amount: `deposit_amount_mismatch` (`cash_in.exception`). An entry ID is credited at most once; repeating it returns the deposit recorded. Requires `transactions:write` and an `Idempotency-Key`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account",
                  "entry_id",
                  "value_date",
                  "amount",
                  "currency"
                ],
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "entry_id": {
                    "type": "string"
                  },
                  "value_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "amount": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "depositor_ref": {
                    "type": "string"
                  },
                  "depositor_email": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "account": "MZ59000800000000123456789",
                "entry_id": "E-1",
                "value_date": "2026-10-08",
                "amount": "150.00",
                "currency": "MZN",
                "depositor_ref": "LOJA-9"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The deposit and its status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "entry_id": {
                      "type": "string"
                    },
                    "value_date": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "depositor_ref": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "deadline_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "reported_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPrgCvLqENtxWLfowO1jP3Y11",
                  "psp_id": "3KWPrdLISOBt6KF1uaGvL8zCqnj",
                  "tenant_id": "3KWPreXWtimMPew1f17LMecHvrj",
                  "account": "MZ59000800000000123456789",
                  "entry_id": "E-1",
                  "value_date": "2026-10-08T00:00:00Z",
                  "amount_minor": 15000,
                  "currency": "MZN",
                  "depositor_ref": "LOJA-9",
                  "mode": "test",
                  "status": "reported",
                  "deadline_at": "2026-10-11T22:15:50.299301615Z",
                  "reported_at": "2026-10-10T22:15:50.299301615Z",
                  "updated_at": "2026-10-10T22:15:50.299301615Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid report; the problems are named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "account is required; entry_id (the statement entry) is required; value_date must be YYYY-MM-DD; a positive amount and a 3-letter currency are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "account is required; entry_id (the statement entry) is required; value_date must be YYYY-MM-DD; a positive amount and a 3-letter currency are required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The key's account is not a PSP.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such cash deposit, account or bank",
                  "error": {
                    "code": "not_found",
                    "message": "no such cash deposit, account or bank"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listCashDeposits",
        "summary": "Cash deposits",
        "description": "Deposits the account reported (as a bank) or was credited with. Requires `transactions:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [deposit], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "account": {
                            "type": "string"
                          },
                          "entry_id": {
                            "type": "string"
                          },
                          "value_date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "depositor_ref": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "credited_account_id": {
                            "type": "string"
                          },
                          "canonical_line_id": {
                            "type": "string"
                          },
                          "statement_amount_minor": {
                            "type": "integer"
                          },
                          "deadline_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "reported_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "resolved_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KgGySgkgWkFmjTto3RmpGVwkny",
                      "psp_id": "3KtGTPT0AQaKTIWS8Xv6tRyJwCs",
                      "tenant_id": "3K75aZx53yJUW0o5JRyLpwMufMq",
                      "account": "0001234567890",
                      "entry_id": "MOV-20261010-000731",
                      "value_date": "2026-10-10T00:00:00Z",
                      "amount_minor": 125000,
                      "currency": "MZN",
                      "depositor_ref": "LOJA-XAI",
                      "mode": "live",
                      "status": "confirmed",
                      "credited_account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "canonical_line_id": "3KPLFLl0qIkFSNvwocczlUevPQQ",
                      "statement_amount_minor": 125000,
                      "deadline_at": "2026-10-13T00:00:00Z",
                      "reported_at": "2026-10-10T10:41:09Z",
                      "resolved_at": "2026-10-11T06:15:27Z",
                      "updated_at": "2026-10-11T06:15:27Z"
                    },
                    {
                      "id": "3KIvQ0y43G8mGSugo8kbjPnKwQx",
                      "psp_id": "3KtGTPT0AQaKTIWS8Xv6tRyJwCs",
                      "tenant_id": "3K75aZx53yJUW0o5JRyLpwMufMq",
                      "account": "0001234567890",
                      "entry_id": "MOV-20261011-000112",
                      "value_date": "2026-10-11T00:00:00Z",
                      "amount_minor": 50000,
                      "currency": "MZN",
                      "depositor_ref": "LOJA-XAI",
                      "mode": "live",
                      "status": "reported",
                      "deadline_at": "2026-10-14T00:00:00Z",
                      "reported_at": "2026-10-11T08:03:55Z",
                      "updated_at": "2026-10-11T08:03:55Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/cash-deposits/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "getCashDeposit",
        "summary": "A cash deposit",
        "description": "Requires `transactions:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deposit.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "entry_id": {
                      "type": "string"
                    },
                    "value_date": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "depositor_ref": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "deadline_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "reported_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPrgCvLqENtxWLfowO1jP3Y11",
                  "psp_id": "3KWPrdLISOBt6KF1uaGvL8zCqnj",
                  "tenant_id": "3KWPreXWtimMPew1f17LMecHvrj",
                  "account": "MZ59000800000000123456789",
                  "entry_id": "E-1",
                  "value_date": "2026-10-08T00:00:00Z",
                  "amount_minor": 15000,
                  "currency": "MZN",
                  "depositor_ref": "LOJA-9",
                  "mode": "test",
                  "status": "reported",
                  "deadline_at": "2026-10-11T22:15:50.299301615Z",
                  "reported_at": "2026-10-10T22:15:50.299301615Z",
                  "updated_at": "2026-10-10T22:15:50.299301615Z"
                }
              }
            }
          },
          "404": {
            "description": "No such deposit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such cash deposit, account or bank",
                  "error": {
                    "code": "not_found",
                    "message": "no such cash deposit, account or bank"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/cash-deposits/{id}/resolve": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "resolveCashDeposit",
        "summary": "Assign or return a suspended deposit",
        "description": "`action` assign (to `account_id`) or return, with a reason; a second person approves (`POST /v1/admin/approvals/{id}/decide`). Requires `reconciliation:write`. A deposit whose credit at the resolver was rejected (`credit_rejected`) is in the same queue; `assign` with a `reference` looks it up again and, once approved, credits what the resolver answers and applies it to the wallet it names (IO-2.6).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "reason"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "assign",
                      "return"
                    ]
                  },
                  "account_id": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string",
                    "description": "With assign: a reference to look up again (IO-2.6), as the partner the cash was deposited with; the deposit goes to what the resolver answers instead of account_id."
                  },
                  "context": {
                    "type": "string",
                    "enum": [
                      "wallet",
                      "meter",
                      "invoice"
                    ],
                    "description": "Narrows the reference (IO-1.6)."
                  }
                }
              },
              "example": {
                "action": "assign",
                "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "cash_in.resolve",
                    "subject_id": "3KAxj9xanJOoXMxIiKgfRm2KJkz",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "payload": "{\"action\":\"assign\",\"account_id\":\"3Ky2WEM0zMfI2pD5i2iBhSEQcB2\"}",
                    "reason": "Depositor reference matches the merchant deposit tag MZ-LOJA-018",
                    "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "status": "pending",
                    "created_at": "2026-10-09T15:12:40Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not suspended, or already pending.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "only a suspended deposit can be assigned or returned",
                  "error": {
                    "code": "invalid_request",
                    "message": "only a suspended deposit can be assigned or returned"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/deposit-tag": {
      "put": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "putDepositTag",
        "summary": "Set an account's deposit tag",
        "description": "A short code (3 to 32 letters, digits or dashes) depositors may quote instead of the account ID; unique. Empty clears it. Records `account.deposit_tag.set`. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "tag"
                ],
                "properties": {
                  "tag": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "tag": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "tag": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "tag": "LOJA-XAI"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or taken.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the tag belongs to another account",
                  "error": {
                    "code": "invalid_request",
                    "message": "the tag belongs to another account"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/safeguarding/accounts": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listSafeguardingAccounts",
        "summary": "Safeguarding accounts",
        "description": "Bank accounts holding client funds (PSP-10.4). Requires `reconciliation:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "account": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "bank": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KoNx2PvLSxvXFC8nNUvulGJMKI",
                      "account": "000800001234567890157",
                      "currency": "MZN",
                      "bank": "BCI",
                      "created_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-02-10T09:00:00Z"
                    },
                    {
                      "id": "3K5ADXkHFgUdUiBg7e5rmAarOGp",
                      "account": "0001234567001",
                      "currency": "MZN",
                      "bank": "Millennium bim",
                      "created_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-02-10T09:05:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "addSafeguardingAccount",
        "summary": "Register a safeguarding account",
        "description": "Its statements (received under a PSP statement profile) feed the daily check. Records `config.safeguarding_account.added`. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account",
                  "currency"
                ],
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "bank": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "account": "2003014567",
                "currency": "MZN",
                "bank": "string"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "bank": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K5ADXkHFgUdUiBg7e5rmAarOGp",
                  "account": "0001234567001",
                  "currency": "MZN",
                  "bank": "Millennium bim",
                  "created_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                  "created_at": "2026-10-11T10:40:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or already registered.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the account is already registered",
                  "error": {
                    "code": "invalid_request",
                    "message": "the account is already registered"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/safeguarding/checks": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listSafeguardingChecks",
        "summary": "Daily safeguarding checks",
        "description": "Each business day, once the safeguarding accounts' statements are in (or by 18:00 UTC), their closing balances are compared per currency with the ledger's client-funds liability (merchant available, pending and payable balances, reserves and suspense; live, collecting model). `compliant` is false on a shortfall of any amount or a missing statement (`safeguarding.check.shortfall`, critical, compliance emailed the same day); else `safeguarding.check.passed`. Requires `reconciliation:read`.",
        "responses": {
          "200": {
            "description": "`{data: [check], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "held_minor": {
                            "type": "integer"
                          },
                          "owed_minor": {
                            "type": "integer"
                          },
                          "difference_minor": {
                            "type": "integer"
                          },
                          "compliant": {
                            "type": "boolean"
                          },
                          "statements": {
                            "type": "string"
                          },
                          "csv_file_id": {
                            "type": "string"
                          },
                          "pdf_file_id": {
                            "type": "string"
                          },
                          "csv_signature": {
                            "type": "string"
                          },
                          "pdf_signature": {
                            "type": "string"
                          },
                          "signing_key_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KHvA6Ef1pziaGSF0wXf4SileRg",
                      "date": "2026-10-10",
                      "currency": "MZN",
                      "held_minor": 1875043250,
                      "owed_minor": 1869210075,
                      "difference_minor": 5833175,
                      "compliant": true,
                      "statements": "[{\"account\":\"000800001234567890157\",\"statement_file_id\":\"3KdhOrsZV8VzDwRsx2nNjQAi6kX\",\"closing_minor\":1250031250,\"received_at\":\"2026-10-10T06:02:11Z\"},{\"account\":\"0001234567001\",\"statement_file_id\":\"3KDLNgOU39cCXqtNJmVrtCTVqzG\",\"closing_minor\":625012000,\"received_at\":\"2026-10-10T06:15:40Z\"}]",
                      "csv_file_id": "3KJ5EjFon9jvDDSDRLTImADqQFz",
                      "pdf_file_id": "3KEKunzWl0OtBzYAL3qgpjqv49A",
                      "csv_signature": "Mf3x9\u2026",
                      "pdf_signature": "q0Lk2\u2026",
                      "signing_key_id": "e11a3b4af7f9e28bb44e6f53a8bd4952",
                      "created_at": "2026-10-10T07:00:03Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/safeguarding/checks/run": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "runSafeguardingCheck",
        "summary": "Run today's check now",
        "description": "For every currency not yet checked today, with the statements at hand. Requires `admin:write`.",
        "responses": {
          "200": {
            "description": "The checks made.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "held_minor": {
                            "type": "integer"
                          },
                          "owed_minor": {
                            "type": "integer"
                          },
                          "difference_minor": {
                            "type": "integer"
                          },
                          "compliant": {
                            "type": "boolean"
                          },
                          "statements": {
                            "type": "string"
                          },
                          "csv_file_id": {
                            "type": "string"
                          },
                          "pdf_file_id": {
                            "type": "string"
                          },
                          "csv_signature": {
                            "type": "string"
                          },
                          "pdf_signature": {
                            "type": "string"
                          },
                          "signing_key_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KO5Pm9L3HOitFWVTYnNF2gUdVD",
                      "date": "2026-10-11",
                      "currency": "MZN",
                      "held_minor": 1250031250,
                      "owed_minor": 1871442080,
                      "difference_minor": -621410830,
                      "compliant": false,
                      "statements": "[{\"account\":\"000800001234567890157\",\"statement_file_id\":\"3KdhOrsZV8VzDwRsx2nNjQAi6kX\",\"closing_minor\":1250031250,\"received_at\":\"2026-10-11T06:02:11Z\"},{\"account\":\"0001234567001\",\"closing_minor\":0,\"missing\":true}]",
                      "csv_file_id": "3KJ5EjFon9jvDDSDRLTImADqQFz",
                      "pdf_file_id": "3KEKunzWl0OtBzYAL3qgpjqv49A",
                      "csv_signature": "Mf3x9\u2026",
                      "pdf_signature": "q0Lk2\u2026",
                      "signing_key_id": "e11a3b4af7f9e28bb44e6f53a8bd4952",
                      "created_at": "2026-10-11T10:45:12Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/safeguarding/checks/{id}/report": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "safeguardingReport",
        "summary": "Signed safeguarding report",
        "description": "The CSV (default) or PDF report, write-once in the region's object storage, with `X-Signature` (base64 Ed25519 over the body) and `X-Signing-Key-ID`. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "pdf"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "The safeguarding check as CSV."
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The safeguarding check as PDF."
                }
              }
            }
          },
          "404": {
            "description": "No such report.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such report",
                  "error": {
                    "code": "not_found",
                    "message": "no such report"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/statement-cycles": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listStatementCycles",
        "summary": "Expected statements",
        "description": "Each statement expected under a profile, per cycle (its local date): the cut-off, the fetch attempts (pull profiles: every 30 minutes for up to 4 hours after the cut-off), the last error, and whether it was received or is missing (recon.file.missing after the arrival window; the PSP's contacts are emailed) (PSP-11.3). Requires `admin:read`.",
        "parameters": [
          {
            "name": "psp_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [cycle], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "profile_id": {
                            "type": "string"
                          },
                          "cycle": {
                            "type": "string"
                          },
                          "cutoff": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "last_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "statement_file_id": {
                            "type": "string"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUZA4WLF1MYP7ZBQSsFJ2HDdN9",
                      "psp_id": "3KUYA7wMf4mYp1zBqSsFj5HdUn3",
                      "profile_id": "eod",
                      "cycle": "2026-10-10",
                      "cutoff": "2026-10-10T21:00:00Z",
                      "status": "received",
                      "attempts": 1,
                      "last_attempt_at": "2026-10-10T23:05:00Z",
                      "statement_file_id": "3KUXZ4vLe1lXo7yApRrEi2GcTm2",
                      "updated_at": "2026-10-10T23:05:13Z"
                    },
                    {
                      "id": "3KUZB7XMG4NZQ1ACRTtGK5IEeO0",
                      "psp_id": "3KUZC1YNH7OAR4BDSUuHL8JFfP1",
                      "profile_id": "eod",
                      "cycle": "2026-10-10",
                      "cutoff": "2026-10-10T21:00:00Z",
                      "status": "missing",
                      "attempts": 4,
                      "last_attempt_at": "2026-10-11T05:00:02Z",
                      "last_error": "sftp: no file matching camt053_20261010*.xml in /outbound",
                      "updated_at": "2026-10-11T05:00:02Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/statement-profiles/{profile_id}/fetch": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "fetchStatementsNow",
        "summary": "Fetch a pull profile now",
        "description": "Reads the PSP's SFTP folder (files matching the profile's pattern, checksums from .sha256 sidecars, the declared host key enforced) or REST API (paginated through items_path and next_path or the Link header, same host only), with the credential from the vault, and ingests what it finds like a pushed file (`recon.file.fetched`). Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profile_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{files, ingested, duplicates, rejected}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "files": {
                      "type": "integer"
                    },
                    "ingested": {
                      "type": "integer"
                    },
                    "duplicates": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "files": 2,
                  "ingested": 1,
                  "duplicates": 1
                }
              }
            }
          },
          "400": {
            "description": "Not a pull profile.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "profile daily-settlement is not a pull profile",
                  "error": {
                    "code": "invalid_request",
                    "message": "profile daily-settlement is not a pull profile"
                  }
                }
              }
            }
          },
          "502": {
            "description": "The PSP could not be reached or answered with an error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Gateway",
                  "status": 502,
                  "code": "upstream_error",
                  "detail": "fetch https://statements.psp.example.co.mz/v1/settlements?date=2026-10-09: HTTP 503",
                  "error": {
                    "code": "upstream_error",
                    "message": "fetch https://statements.psp.example.co.mz/v1/settlements?date=2026-10-09: HTTP 503"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}/match": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "matchStatementFile",
        "summary": "Match a statement",
        "description": "Runs the ordered matching rules on the statement's unmatched lines (credits to the PSP's payments, debits to its payouts); the first that matches wins: exact (PSP reference, amount, currency), kulpay_ref (the echoed KulPay reference and amount), fuzzy (the payer's last four digits, amount, time within the profile's window, default 10 minutes), many_to_one (one bank credit for a value date's transactions summing to it), tolerance (a reference match within the profile's threshold, the difference booked to an adjustment account). More than one candidate raises ambiguous_match. Idempotent: matched lines and transactions are never matched again. Runs on every ingest and re-parse too (`recon.match.auto`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{lines, already_matched, matched (by rule), ambiguous, unmatched}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lines": {
                      "type": "integer"
                    },
                    "already_matched": {
                      "type": "integer"
                    },
                    "matched": {
                      "type": "object",
                      "properties": {
                        "exact": {
                          "type": "integer"
                        },
                        "kulpay_ref": {
                          "type": "integer"
                        }
                      }
                    },
                    "ambiguous": {
                      "type": "integer"
                    },
                    "unmatched": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "lines": 42,
                  "already_matched": 30,
                  "matched": {
                    "exact": 8,
                    "kulpay_ref": 2
                  },
                  "ambiguous": 1,
                  "unmatched": 1
                }
              }
            }
          },
          "404": {
            "description": "No such statement.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such statement",
                  "error": {
                    "code": "not_found",
                    "message": "no such statement"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/statements/{id}/matches": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "statementFileMatches",
        "summary": "A statement's matches",
        "description": "Each match with its rule, confidence, difference, lines (by `line_key`, account:line_id) and transactions. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [match], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "statement_file_id": {
                            "type": "string"
                          },
                          "rule_id": {
                            "type": "string"
                          },
                          "confidence": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "lines": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "line_key": {
                                  "type": "string"
                                },
                                "match_id": {
                                  "type": "string"
                                },
                                "canonical_line_id": {
                                  "type": "string"
                                },
                                "amount_minor": {
                                  "type": "integer"
                                }
                              }
                            }
                          },
                          "transactions": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "transaction_type": {
                                  "type": "string"
                                },
                                "transaction_id": {
                                  "type": "string"
                                },
                                "match_id": {
                                  "type": "string"
                                },
                                "amount_minor": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUYF4BQk1rDu7EGvXxKo2MiZs8",
                      "psp_id": "3KUYA7wMf4mYp1zBqSsFj5HdUn3",
                      "tenant_id": "3KUYB1xNg7nZq4ACrTtGk8IeVo4",
                      "statement_file_id": "3KUXZ4vLe1lXo7yApRrEi2GcTm2",
                      "rule_id": "exact",
                      "confidence": 100,
                      "created_at": "2026-10-11T08:40:03Z",
                      "lines": [
                        {
                          "line_key": "000100000012345678957:NTRY-20261010-0007",
                          "match_id": "3KUYF4BQk1rDu7EGvXxKo2MiZs8",
                          "canonical_line_id": "3KUYG7CRl4sEv1FHwYyLp5NjAt9",
                          "amount_minor": 125075
                        }
                      ],
                      "transactions": [
                        {
                          "transaction_type": "payment",
                          "transaction_id": "3KUYH1DSm7tFw4GIxZzMq8OkBu0",
                          "match_id": "3KUYF4BQk1rDu7EGvXxKo2MiZs8",
                          "amount_minor": 125075
                        }
                      ]
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/matches": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "manualMatch",
        "summary": "Match by hand",
        "description": "Links statement lines (`line_keys`) to payments and payouts with a reason; credits and payments, debits and payouts must add up. Closes open exceptions about the lines (`recon.match.manual`, `recon.exception.resolved`). Requires `reconciliation:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "line_keys",
                  "reason"
                ],
                "properties": {
                  "line_keys": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "payment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "payout_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "line_keys": [
                  "invoice_number"
                ],
                "payment_ids": [
                  "3KUXQ43bztiuf9tCMNgbcrYvne3"
                ],
                "payout_ids": [
                  "3KUXQ43bztiuf9tCMNgbcrYvne3"
                ],
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The match.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "statement_file_id": {
                      "type": "string"
                    },
                    "rule_id": {
                      "type": "string"
                    },
                    "confidence": {
                      "type": "integer"
                    },
                    "actor": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KzLf5hEJPVMYSPRNgzUI0mveiR",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "tenant_id": "3K12JLy9OnIwuN9yzoIgIKQpHJX",
                  "statement_file_id": "3KdhOrsZV8VzDwRsx2nNjQAi6kX",
                  "rule_id": "manual",
                  "confidence": 100,
                  "actor": "3KIhzpLdNupTuLgu8bwZOQspar2",
                  "reason": "Bank grouped two M-Pesa collections into one credit",
                  "created_at": "2026-10-11T11:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Already matched, amounts that do not add up, or unknown items.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the lines (340025) and the transactions (325000) do not add up",
                  "error": {
                    "code": "invalid_request",
                    "message": "the lines (340025) and the transactions (325000) do not add up"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/exceptions": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listExceptions",
        "summary": "Reconciliation exceptions",
        "description": "The queue (PSP-11.7): each item reconciliation could not settle, typed (missing_at_psp, missing_at_kulpay, status_mismatch, amount_mismatch, currency_mismatch, fee_variance, settlement_shortfall, late_settlement, duplicate, ambiguous_match, unidentified_deposit, unconfirmed_deposit, deposit_amount_mismatch), with its state (open, investigating, resolved, written_off), severity, owner and due date (2 working days by default). `amount_minor` is signed from KulPay's side: negative when KulPay is short. Overdue items are escalated to the finance lead (`recon.exception.escalated`). Operators use `/v1/admin/reconciliation/exceptions`. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "owner",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "open",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [exception], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "line_key": {
                            "type": "string"
                          },
                          "subject_type": {
                            "type": "string"
                          },
                          "subject_id": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          },
                          "due_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                      "type": "amount_mismatch",
                      "status": "open",
                      "severity": "medium",
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                      "subject_type": "payment",
                      "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                      "amount_minor": -2500,
                      "currency": "MZN",
                      "detail": "statement 1225.75, KulPay 1250.75",
                      "due_at": "2026-10-13T08:00:00Z",
                      "created_at": "2026-10-09T08:00:00Z",
                      "updated_at": "2026-10-09T08:00:00Z"
                    },
                    {
                      "id": "3KQ9sKh890YzZiBgbnLY4Qxegts",
                      "type": "missing_at_psp",
                      "status": "investigating",
                      "severity": "high",
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "subject_type": "payment",
                      "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                      "amount_minor": 75000,
                      "currency": "MZN",
                      "detail": "captured 2026-10-05, no statement line after 72 hours",
                      "due_at": "2026-10-12T08:00:00Z",
                      "created_at": "2026-10-08T08:00:00Z",
                      "updated_at": "2026-10-08T11:30:00Z",
                      "owner": "ana.macuacua@example.co.mz"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/exceptions/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "getException",
        "summary": "An exception with its comments",
        "description": "Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The exception with `comments`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "severity": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "line_key": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string"
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "due_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "owner": {
                      "type": "string"
                    },
                    "comments": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "exception_id": {
                            "type": "string"
                          },
                          "author": {
                            "type": "string"
                          },
                          "text": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                  "type": "amount_mismatch",
                  "status": "investigating",
                  "severity": "medium",
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                  "subject_type": "payment",
                  "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                  "amount_minor": -2500,
                  "currency": "MZN",
                  "detail": "statement 1225.75, KulPay 1250.75",
                  "due_at": "2026-10-13T08:00:00Z",
                  "created_at": "2026-10-09T08:00:00Z",
                  "updated_at": "2026-10-09T10:05:00Z",
                  "owner": "ana.macuacua@example.co.mz",
                  "comments": [
                    {
                      "id": "3KPmitoPLBQfZe7flLiMk5F59md",
                      "exception_id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                      "author": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                      "text": "The PSP deducted a 25.00 MZN fee at source; asked them to confirm.",
                      "created_at": "2026-10-09T10:07:21Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such exception.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such exception",
                  "error": {
                    "code": "not_found",
                    "message": "no such exception"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/exceptions/{id}/assign": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "assignException",
        "summary": "Assign an owner",
        "description": "An open exception moves to investigating (`recon.exception.assigned`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "owner"
                ],
                "properties": {
                  "owner": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "owner": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The exception.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "severity": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "line_key": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string"
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "detail": {
                      "type": "string"
                    },
                    "due_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "owner": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                  "type": "amount_mismatch",
                  "status": "investigating",
                  "severity": "medium",
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                  "subject_type": "payment",
                  "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                  "amount_minor": -2500,
                  "currency": "MZN",
                  "detail": "statement 1225.75, KulPay 1250.75",
                  "due_at": "2026-10-13T08:00:00Z",
                  "created_at": "2026-10-09T08:00:00Z",
                  "updated_at": "2026-10-09T10:05:00Z",
                  "owner": "ana.macuacua@example.co.mz"
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/exceptions/{id}/comments": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "commentException",
        "summary": "Comment",
        "description": "Comments are only added, never edited (`recon.exception.commented`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "text": "string"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The comment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "exception_id": {
                      "type": "string"
                    },
                    "author": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KPmitoPLBQfZe7flLiMk5F59md",
                  "exception_id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                  "author": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                  "text": "The PSP deducted a 25.00 MZN fee at source; asked them to confirm.",
                  "created_at": "2026-10-09T10:07:21Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/reconciliation/exceptions/{id}/actions": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "actOnException",
        "summary": "Act on an exception",
        "description": "`claim` raises a claim with the PSP (`recon.exception.claimed`; stays under investigation); `compensate` records a compensating event and resolves; `resolve` closes an explained item; `write_off` (the amount to the recon_write_off account against the PSP receivable) and `return_deposit` move money and wait for a second person (202 with the approval). Records are never edited; matching by hand is `POST /v1/reconciliation/matches`. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "reason"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "claim",
                      "compensate",
                      "resolve",
                      "write_off",
                      "return_deposit"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "claim",
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{exception}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "line_key": {
                          "type": "string"
                        },
                        "subject_type": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "amount_minor": {
                          "type": "integer"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "detail": {
                          "type": "string"
                        },
                        "due_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "resolution": {
                          "type": "string"
                        },
                        "resolved_by": {
                          "type": "string"
                        },
                        "resolved_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "exception": {
                    "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                    "type": "amount_mismatch",
                    "status": "resolved",
                    "severity": "medium",
                    "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                    "subject_type": "payment",
                    "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                    "amount_minor": -2500,
                    "currency": "MZN",
                    "detail": "statement 1225.75, KulPay 1250.75",
                    "due_at": "2026-10-13T08:00:00Z",
                    "created_at": "2026-10-09T08:00:00Z",
                    "updated_at": "2026-10-09T14:22:05Z",
                    "resolution": "resolve: fee deducted at source, confirmed by the PSP",
                    "resolved_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "resolved_at": "2026-10-09T14:22:05Z"
                  }
                }
              }
            }
          },
          "202": {
            "description": "`{exception, approval}`: waiting for a second person.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "severity": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "line_key": {
                          "type": "string"
                        },
                        "subject_type": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "amount_minor": {
                          "type": "integer"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "detail": {
                          "type": "string"
                        },
                        "due_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "exception": {
                    "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                    "type": "amount_mismatch",
                    "status": "open",
                    "severity": "medium",
                    "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                    "subject_type": "payment",
                    "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                    "amount_minor": -2500,
                    "currency": "MZN",
                    "detail": "statement 1225.75, KulPay 1250.75",
                    "due_at": "2026-10-13T08:00:00Z",
                    "created_at": "2026-10-09T08:00:00Z",
                    "updated_at": "2026-10-09T08:00:00Z"
                  },
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "recon.exception.write_off",
                    "subject_id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "payload": "{\"amount_minor\":-2500,\"currency\":\"MZN\"}",
                    "reason": "Fee shortfall below the claim threshold",
                    "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "status": "pending",
                    "created_at": "2026-10-09T14:22:05Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Closed, or not possible for this exception.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the exception is resolved",
                  "error": {
                    "code": "invalid_request",
                    "message": "the exception is resolved"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/psp-settlements": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "reportSettlement",
        "summary": "Send a settlement report",
        "description": "A PSP reports what it settled for a day (`gross`, `fees`, `net`, `value_date` when the net reaches KulPay; default the date plus the PSP's settlement cycle). Level 2: compared with the day's captured payments through the PSP and its contracted fees; a fee difference raises `fee_variance`, a short gross `settlement_shortfall`, a gross above `amount_mismatch` (`recon.settlement.matched` with status matched or variance). Level 3: the net is matched to a bank statement credit on the value date (`recon.cash.matched`); none a day later raises `late_settlement`. With `batch_id`, the PSP or the merchant reports on a direct-model settlement batch, which becomes verified (or variance); a direct batch with no report within 7 days is marked unverified. Requires `reconciliation:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "report_id",
                  "date",
                  "currency",
                  "gross",
                  "fees",
                  "net"
                ],
                "properties": {
                  "report_id": {
                    "type": "string"
                  },
                  "date": {
                    "type": "string",
                    "format": "date"
                  },
                  "value_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "currency": {
                    "type": "string"
                  },
                  "gross": {
                    "type": "string"
                  },
                  "fees": {
                    "type": "string"
                  },
                  "net": {
                    "type": "string"
                  },
                  "batch_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "report_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "date": "2026-10-31",
                "value_date": "2026-10-31",
                "currency": "MZN",
                "gross": "string",
                "fees": "1250.00",
                "net": "string",
                "batch_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The report with its expected amounts and statuses; a resent report_id returns the first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "report_id": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "date": {
                      "type": "string"
                    },
                    "expected_value_date": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "gross_minor": {
                      "type": "integer"
                    },
                    "fees_minor": {
                      "type": "integer"
                    },
                    "net_minor": {
                      "type": "integer"
                    },
                    "expected_gross_minor": {
                      "type": "integer"
                    },
                    "expected_fees_minor": {
                      "type": "integer"
                    },
                    "expected_net_minor": {
                      "type": "integer"
                    },
                    "transactions": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "cash_status": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K2jGKmMo7A0Toqvwj0XqAJc8rT",
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "report_id": "MPESA-SETT-20261009",
                  "tenant_id": "3K01AFCO8zayIwIHSwO2tlZRWMR",
                  "date": "2026-10-09",
                  "expected_value_date": "2026-10-10T00:00:00Z",
                  "currency": "MZN",
                  "gross_minor": 18745075,
                  "fees_minor": 187451,
                  "net_minor": 18557624,
                  "expected_gross_minor": 18745075,
                  "expected_fees_minor": 187451,
                  "expected_net_minor": 18557624,
                  "transactions": 142,
                  "status": "matched",
                  "cash_status": "pending",
                  "created_at": "2026-10-10T06:05:12Z",
                  "updated_at": "2026-10-10T06:05:12Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid, or gross less fees is not net.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "gross less fees must equal net",
                  "error": {
                    "code": "invalid_request",
                    "message": "gross less fees must equal net"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listSettlementReports",
        "summary": "Settlement reports",
        "description": "The account's reports. Requires `reconciliation:read`.",
        "responses": {
          "200": {
            "description": "`{data: [report], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "report_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "date": {
                            "type": "string"
                          },
                          "expected_value_date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "gross_minor": {
                            "type": "integer"
                          },
                          "fees_minor": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          },
                          "expected_gross_minor": {
                            "type": "integer"
                          },
                          "expected_fees_minor": {
                            "type": "integer"
                          },
                          "expected_net_minor": {
                            "type": "integer"
                          },
                          "transactions": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          },
                          "cash_status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K2jGKmMo7A0Toqvwj0XqAJc8rT",
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "report_id": "MPESA-SETT-20261009",
                      "tenant_id": "3K01AFCO8zayIwIHSwO2tlZRWMR",
                      "date": "2026-10-09",
                      "expected_value_date": "2026-10-10T00:00:00Z",
                      "currency": "MZN",
                      "gross_minor": 18745075,
                      "fees_minor": 187451,
                      "net_minor": 18557624,
                      "expected_gross_minor": 18745075,
                      "expected_fees_minor": 187451,
                      "expected_net_minor": 18557624,
                      "transactions": 142,
                      "status": "matched",
                      "cash_status": "pending",
                      "created_at": "2026-10-10T06:05:12Z",
                      "updated_at": "2026-10-10T06:05:12Z"
                    },
                    {
                      "id": "3KHb6TqW2mZx9RcV4nLp7YsEdGf",
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "report_id": "MPESA-SETT-20261008",
                      "tenant_id": "3K01AFCO8zayIwIHSwO2tlZRWMR",
                      "date": "2026-10-08",
                      "expected_value_date": "2026-10-09T00:00:00Z",
                      "currency": "MZN",
                      "gross_minor": 15230050,
                      "fees_minor": 150000,
                      "net_minor": 15080050,
                      "expected_gross_minor": 15230050,
                      "expected_fees_minor": 152301,
                      "expected_net_minor": 15077749,
                      "transactions": 118,
                      "status": "variance",
                      "cash_status": "matched",
                      "created_at": "2026-10-09T06:04:47Z",
                      "updated_at": "2026-10-09T13:40:02Z",
                      "cash_line_key": "3K01AFCO8zayIwIHSwO2tlZRWMR:3KRw5Yp8TnQz2XmVc7LbJ4sHdKe",
                      "cash_matched_at": "2026-10-09T13:40:02Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/fee-claims": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "feeClaims",
        "summary": "Fee claim file",
        "description": "Open fee variances as a CSV claim for the PSP: report, day, fees charged, fees contracted, overcharge. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "psp_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/csv.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "Fee claims as CSV (fee-claims.csv)."
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/health": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "reconHealth",
        "summary": "Reconciliation dashboard",
        "description": "Per PSP in this region, for lines valued in the window (default the last 30 days): lines, auto-matched and manually matched, the auto-match rate against its 99.5% target, open exceptions by type and age (under_1d, 1_2d, 2_5d, over_5d), open variance amounts per currency and missing statements. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "region": {
                      "type": "string"
                    },
                    "from": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "to": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "target": {
                      "type": "number"
                    },
                    "psps": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "psp_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "lines": {
                            "type": "integer"
                          },
                          "auto_matched": {
                            "type": "integer"
                          },
                          "manual_matched": {
                            "type": "integer"
                          },
                          "auto_match_rate": {
                            "type": "number"
                          },
                          "on_target": {
                            "type": "boolean"
                          },
                          "open_by_type": {
                            "type": "object",
                            "properties": {
                              "amount_mismatch": {
                                "type": "integer"
                              }
                            }
                          },
                          "open_by_age": {
                            "type": "object",
                            "properties": {
                              "1_2d": {
                                "type": "integer"
                              }
                            }
                          },
                          "variance_minor": {
                            "type": "object",
                            "properties": {
                              "MZN": {
                                "type": "integer"
                              }
                            }
                          },
                          "missing_files": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "region": "mz",
                  "from": "2026-09-12T00:00:00Z",
                  "to": "2026-10-12T00:00:00Z",
                  "target": 99.5,
                  "psps": [
                    {
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "name": "Carteira Movel Exemplo, SA",
                      "lines": 4210,
                      "auto_matched": 4195,
                      "manual_matched": 9,
                      "auto_match_rate": 99.6437054631829,
                      "on_target": true,
                      "open_by_type": {
                        "amount_mismatch": 1
                      },
                      "open_by_age": {
                        "1_2d": 1
                      },
                      "variance_minor": {
                        "MZN": 2500
                      },
                      "missing_files": 0
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/periods": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listPeriods",
        "summary": "Month-end closes",
        "description": "Requires `reconciliation:read`.",
        "responses": {
          "200": {
            "description": "`{data: [period], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "period": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "checklist": {
                            "type": "string"
                          },
                          "carry_forward": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "prepared_by": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KqF4DS5t7ewYObBjx7w9OA8b9w",
                      "period": "2026-09",
                      "status": "closing",
                      "checklist": "{\"period\":\"2026-09\",\"ready\":true,\"items\":[{\"key\":\"files_received\",\"passed\":true,\"detail\":\"every expected statement arrived\"},{\"key\":\"ledger_balanced\",\"passed\":true,\"detail\":\"every ledger account equals its journal lines\"},{\"key\":\"safeguarding_passed\",\"passed\":true,\"detail\":\"the safeguarding check passed every business day\"},{\"key\":\"exceptions_settled\",\"passed\":true,\"detail\":\"every exception resolved or carried forward with a reason\"}],\"open_exceptions\":[{\"id\":\"3KQ9sKh890YzZiBgbnLY4Qxegts\",\"type\":\"fee_variance\",\"status\":\"open\",\"severity\":\"medium\",\"psp_id\":\"3KARihnYbNSj5SoAHKBQV7Bw0uO\",\"tenant_id\":\"3Ky2WEM0zMfI2pD5i2iBhSEQcB2\",\"subject_type\":\"settlement\",\"subject_id\":\"3K2jGKmMo7A0Toqvwj0XqAJc8rT\",\"amount_minor\":4500,\"currency\":\"MZN\",\"detail\":\"fees 31500, the contract says 36000\",\"due_at\":\"2026-10-01T08:00:00Z\",\"created_at\":\"2026-09-29T08:00:00Z\",\"updated_at\":\"2026-09-29T08:00:00Z\"}]}",
                      "carry_forward": "{\"3KQ9sKh890YzZiBgbnLY4Qxegts\":\"PSP confirmed the fee refund in the October statement\"}",
                      "reason": "September 2026 close: all statements in, one fee variance carried forward",
                      "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                      "approval_id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                      "created_at": "2026-10-05T10:12:44Z",
                      "updated_at": "2026-10-05T10:12:44Z"
                    },
                    {
                      "id": "3KIn0SVEDYMf7V5enlkvdoCc3Nf",
                      "period": "2026-08",
                      "status": "closed",
                      "checklist": "{\"period\":\"2026-08\",\"ready\":true,\"items\":[{\"key\":\"files_received\",\"passed\":true,\"detail\":\"every expected statement arrived\"},{\"key\":\"ledger_balanced\",\"passed\":true,\"detail\":\"every ledger account equals its journal lines\"},{\"key\":\"safeguarding_passed\",\"passed\":true,\"detail\":\"the safeguarding check passed every business day\"},{\"key\":\"exceptions_settled\",\"passed\":true,\"detail\":\"every exception resolved or carried forward with a reason\"}]}",
                      "carry_forward": "{}",
                      "reason": "August 2026 close",
                      "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                      "approval_id": "3KTqZyQ1v8nWm4XcE7pLs2RbHdA",
                      "approved_by": "ana.macuacua@example.co.mz",
                      "csv_file_id": "3KLm8Rt2YwQe5VnZpX4cB7dHsJa",
                      "pdf_file_id": "3KNp3Wq7ZrTy6XmBv2cL9fGhKsE",
                      "csv_signature": "MEUCIQDx\u2026",
                      "pdf_signature": "MEQCIF3k\u2026",
                      "signing_key_id": "recon-report-2026",
                      "closed_at": "2026-09-04T14:20:31Z",
                      "created_at": "2026-09-03T09:01:10Z",
                      "updated_at": "2026-09-04T14:20:31Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/periods/{period}/checklist": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "periodChecklist",
        "summary": "Month-end checklist",
        "description": "files_received (no statement missing), ledger_balanced (every account equals its lines), safeguarding_passed (every business day, every currency), exceptions_settled (open exceptions need a reason to be carried forward). Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "period",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "2026-09"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The checklist and the open exceptions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "period": {
                      "type": "string"
                    },
                    "ready": {
                      "type": "boolean"
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "passed": {
                            "type": "boolean"
                          },
                          "detail": {
                            "type": "string"
                          },
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "open_exceptions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "severity": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "tenant_id": {
                            "type": "string"
                          },
                          "line_key": {
                            "type": "string"
                          },
                          "subject_type": {
                            "type": "string"
                          },
                          "subject_id": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          },
                          "due_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "period": "2026-09",
                  "ready": false,
                  "items": [
                    {
                      "key": "files_received",
                      "passed": false,
                      "detail": "1 statement(s) missing",
                      "items": [
                        "3KARihnYbNSj5SoAHKBQV7Bw0uO/daily-settlement 2026-09-30"
                      ]
                    },
                    {
                      "key": "ledger_balanced",
                      "passed": true,
                      "detail": "every ledger account equals its journal lines"
                    },
                    {
                      "key": "safeguarding_passed",
                      "passed": true,
                      "detail": "the safeguarding check passed every business day"
                    },
                    {
                      "key": "exceptions_settled",
                      "passed": false,
                      "detail": "1 open exception(s) need a reason to be carried forward",
                      "items": [
                        "3KvSzSsEafBY1dWtaoDbv58kH2A amount_mismatch"
                      ]
                    }
                  ],
                  "open_exceptions": [
                    {
                      "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                      "type": "amount_mismatch",
                      "status": "open",
                      "severity": "medium",
                      "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                      "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                      "subject_type": "payment",
                      "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                      "amount_minor": -2500,
                      "currency": "MZN",
                      "detail": "statement 1225.75, KulPay 1250.75",
                      "due_at": "2026-09-30T08:00:00Z",
                      "created_at": "2026-09-28T08:00:00Z",
                      "updated_at": "2026-09-28T08:00:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/periods/{period}/close": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "closePeriod",
        "summary": "Prepare a month-end close",
        "description": "After the month ends, with the checklist passing (open exceptions carried forward with a reason each, or `carry_all`), the close waits for a second person (`POST /v1/admin/approvals/{id}/decide`). On sign-off the checklist runs again, the signed report (CSV, PDF) is stored write-once and the period is locked: ledger postings dated in it land in the next open period as adjustments (`recon.period.closed`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "period",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "2026-09"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  },
                  "carry_forward": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "carry_all": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Pagamento de energia, outubro",
                "carry_forward": {
                  "key": "1250.00"
                },
                "carry_all": "string"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval, checklist}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "checklist": {
                      "type": "object",
                      "properties": {
                        "period": {
                          "type": "string"
                        },
                        "ready": {
                          "type": "boolean"
                        },
                        "items": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string"
                              },
                              "passed": {
                                "type": "boolean"
                              },
                              "detail": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "open_exceptions": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              },
                              "status": {
                                "type": "string"
                              },
                              "severity": {
                                "type": "string"
                              },
                              "psp_id": {
                                "type": "string"
                              },
                              "tenant_id": {
                                "type": "string"
                              },
                              "subject_type": {
                                "type": "string"
                              },
                              "subject_id": {
                                "type": "string"
                              },
                              "amount_minor": {
                                "type": "integer"
                              },
                              "currency": {
                                "type": "string"
                              },
                              "detail": {
                                "type": "string"
                              },
                              "due_at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "created_at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "updated_at": {
                                "type": "string",
                                "format": "date-time"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "recon.period.close",
                    "subject_id": "3KqF4DS5t7ewYObBjx7w9OA8b9w",
                    "tenant_id": "platform",
                    "payload": "{\"period\":\"2026-09\"}",
                    "reason": "September 2026 close: all statements in, one fee variance carried forward",
                    "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "status": "pending",
                    "created_at": "2026-10-05T10:12:44Z"
                  },
                  "checklist": {
                    "period": "2026-09",
                    "ready": true,
                    "items": [
                      {
                        "key": "files_received",
                        "passed": true,
                        "detail": "every expected statement arrived"
                      },
                      {
                        "key": "ledger_balanced",
                        "passed": true,
                        "detail": "every ledger account equals its journal lines"
                      },
                      {
                        "key": "safeguarding_passed",
                        "passed": true,
                        "detail": "the safeguarding check passed every business day"
                      },
                      {
                        "key": "exceptions_settled",
                        "passed": true,
                        "detail": "every exception resolved or carried forward with a reason"
                      }
                    ],
                    "open_exceptions": [
                      {
                        "id": "3KQ9sKh890YzZiBgbnLY4Qxegts",
                        "type": "fee_variance",
                        "status": "open",
                        "severity": "medium",
                        "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                        "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                        "subject_type": "settlement",
                        "subject_id": "3K2jGKmMo7A0Toqvwj0XqAJc8rT",
                        "amount_minor": 4500,
                        "currency": "MZN",
                        "detail": "fees 31500, the contract says 36000",
                        "due_at": "2026-10-01T08:00:00Z",
                        "created_at": "2026-09-29T08:00:00Z",
                        "updated_at": "2026-09-29T08:00:00Z"
                      }
                    ]
                  }
                }
              }
            }
          },
          "409": {
            "description": "The checklist does not pass (`not_ready`), with the checklist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_ready",
                  "detail": "the checklist does not pass",
                  "checklist": {
                    "period": "2026-09",
                    "ready": false,
                    "items": [
                      {
                        "key": "files_received",
                        "passed": false,
                        "detail": "1 statement(s) missing",
                        "items": [
                          "3KARihnYbNSj5SoAHKBQV7Bw0uO/daily-settlement 2026-09-30"
                        ]
                      },
                      {
                        "key": "ledger_balanced",
                        "passed": true,
                        "detail": "every ledger account equals its journal lines"
                      },
                      {
                        "key": "safeguarding_passed",
                        "passed": true,
                        "detail": "the safeguarding check passed every business day"
                      },
                      {
                        "key": "exceptions_settled",
                        "passed": false,
                        "detail": "1 open exception(s) need a reason to be carried forward",
                        "items": [
                          "3KvSzSsEafBY1dWtaoDbv58kH2A amount_mismatch"
                        ]
                      }
                    ],
                    "open_exceptions": [
                      {
                        "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                        "type": "amount_mismatch",
                        "status": "open",
                        "severity": "medium",
                        "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                        "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                        "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                        "subject_type": "payment",
                        "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                        "amount_minor": -2500,
                        "currency": "MZN",
                        "detail": "statement 1225.75, KulPay 1250.75",
                        "due_at": "2026-09-30T08:00:00Z",
                        "created_at": "2026-09-28T08:00:00Z",
                        "updated_at": "2026-09-28T08:00:00Z"
                      }
                    ]
                  },
                  "error": {
                    "code": "not_ready",
                    "message": "the checklist does not pass",
                    "checklist": {
                      "period": "2026-09",
                      "ready": false,
                      "items": [
                        {
                          "key": "files_received",
                          "passed": false,
                          "detail": "1 statement(s) missing",
                          "items": [
                            "3KARihnYbNSj5SoAHKBQV7Bw0uO/daily-settlement 2026-09-30"
                          ]
                        },
                        {
                          "key": "ledger_balanced",
                          "passed": true,
                          "detail": "every ledger account equals its journal lines"
                        },
                        {
                          "key": "safeguarding_passed",
                          "passed": true,
                          "detail": "the safeguarding check passed every business day"
                        },
                        {
                          "key": "exceptions_settled",
                          "passed": false,
                          "detail": "1 open exception(s) need a reason to be carried forward",
                          "items": [
                            "3KvSzSsEafBY1dWtaoDbv58kH2A amount_mismatch"
                          ]
                        }
                      ],
                      "open_exceptions": [
                        {
                          "id": "3KvSzSsEafBY1dWtaoDbv58kH2A",
                          "type": "amount_mismatch",
                          "status": "open",
                          "severity": "medium",
                          "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                          "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                          "line_key": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2:3KS4kWwcWwYGrimwzV2vQkmxyYX",
                          "subject_type": "payment",
                          "subject_id": "3K3JBjiqls3SW7LzRaZUmOjyXgA",
                          "amount_minor": -2500,
                          "currency": "MZN",
                          "detail": "statement 1225.75, KulPay 1250.75",
                          "due_at": "2026-09-30T08:00:00Z",
                          "created_at": "2026-09-28T08:00:00Z",
                          "updated_at": "2026-09-28T08:00:00Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/reconciliation/periods/{period}/report": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "periodReport",
        "summary": "Signed month-end report",
        "description": "CSV (default) or PDF, with `X-Signature` and `X-Signing-Key-ID`. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "period",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "2026-09"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv",
                "pdf"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "The signed period report as CSV."
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary",
                  "description": "The signed period report as PDF."
                }
              }
            }
          },
          "404": {
            "description": "Not closed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no report for this period",
                  "error": {
                    "code": "not_found",
                    "message": "no report for this period"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/templates": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listRegulatoryTemplates",
        "summary": "Regulatory report templates",
        "description": "Requires `reconciliation:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string"
                          },
                          "fields": {
                            "type": "string"
                          },
                          "group_by": {
                            "type": "string"
                          },
                          "period": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          },
                          "active": {
                            "type": "boolean"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                      "code": "BM-PAY-DAILY",
                      "name": "Pagamentos di\u00e1rios por PSP e m\u00e9todo",
                      "source": "payments",
                      "fields": "[\"date\",\"psp_id\",\"method\",\"count\",\"amount\",\"fees\"]",
                      "group_by": "[\"date\",\"psp_id\",\"method\"]",
                      "period": "daily",
                      "format": "csv",
                      "active": true,
                      "version": 2,
                      "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                      "created_at": "2026-03-02T09:15:00Z",
                      "updated_at": "2026-06-18T14:40:12Z"
                    },
                    {
                      "id": "3KpIXlMpv6U6uHUiJY7dFShREqZ",
                      "code": "BM-LEDGER-MONTHLY",
                      "name": "Movimentos do raz\u00e3o por conta",
                      "source": "ledger",
                      "fields": "[\"account_code\",\"currency\",\"count\",\"debit\",\"credit\"]",
                      "group_by": "[\"account_code\",\"currency\"]",
                      "period": "monthly",
                      "format": "xlsx",
                      "active": true,
                      "version": 1,
                      "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                      "created_at": "2026-04-01T08:00:00Z",
                      "updated_at": "2026-04-01T08:00:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "createRegulatoryTemplate",
        "summary": "Create a regulatory report template",
        "description": "A configurable report (PSP-10.6): source, grouping, fields, period and format. The exact list Banco de Mo\u00e7ambique requires is to be confirmed with compliance; reports are configured, not coded. Records `config.regulatory_template.saved`. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "name",
                  "source",
                  "period",
                  "format",
                  "fields"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "payments",
                      "ledger"
                    ]
                  },
                  "period": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "monthly",
                      "on_demand"
                    ]
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "xlsx",
                      "xml"
                    ]
                  },
                  "group_by": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "payments: date, psp_id, method, currency; ledger: date, tenant_id, currency, account_code, kind."
                  },
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Grouped dimensions and measures (payments: count, amount, fees; ledger: count, debit, credit)."
                  },
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "code": "EDM",
                "name": "Electricidade de Mo\u00e7ambique",
                "source": "payments",
                "period": "daily",
                "format": "csv",
                "group_by": [
                  "string"
                ],
                "fields": [
                  "money.amount"
                ],
                "active": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    },
                    "fields": {
                      "type": "string"
                    },
                    "group_by": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "format": {
                      "type": "string"
                    },
                    "active": {
                      "type": "boolean"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                  "code": "BM-PAY-DAILY",
                  "name": "Pagamentos di\u00e1rios por PSP e m\u00e9todo",
                  "source": "payments",
                  "fields": "[\"date\",\"psp_id\",\"method\",\"count\",\"amount\",\"fees\"]",
                  "group_by": "[\"date\",\"psp_id\",\"method\"]",
                  "period": "daily",
                  "format": "csv",
                  "active": true,
                  "version": 1,
                  "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                  "created_at": "2026-03-02T09:15:00Z",
                  "updated_at": "2026-03-02T09:15:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid; the problems are named.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "period must be daily, monthly or on_demand; field \"tenant_id\" is neither a grouped dimension nor a payments measure (count, amount, fees)",
                  "error": {
                    "code": "invalid_request",
                    "message": "period must be daily, monthly or on_demand; field \"tenant_id\" is neither a grouped dimension nor a payments measure (count, amount, fees)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/templates/{id}": {
      "put": {
        "tags": [
          "Ledger"
        ],
        "operationId": "updateRegulatoryTemplate",
        "summary": "New version of a template",
        "description": "Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "name",
                  "source",
                  "period",
                  "format",
                  "fields"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "source": {
                    "type": "string",
                    "enum": [
                      "payments",
                      "ledger"
                    ]
                  },
                  "period": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "monthly",
                      "on_demand"
                    ]
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "xlsx",
                      "xml"
                    ]
                  },
                  "group_by": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "payments: date, psp_id, method, currency; ledger: date, tenant_id, currency, account_code, kind."
                  },
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Grouped dimensions and measures (payments: count, amount, fees; ledger: count, debit, credit)."
                  },
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "code": "EDM",
                "name": "Electricidade de Mo\u00e7ambique",
                "source": "payments",
                "period": "daily",
                "format": "csv",
                "group_by": [
                  "string"
                ],
                "fields": [
                  "money.amount"
                ],
                "active": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The template, version incremented.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    },
                    "fields": {
                      "type": "string"
                    },
                    "group_by": {
                      "type": "string"
                    },
                    "period": {
                      "type": "string"
                    },
                    "format": {
                      "type": "string"
                    },
                    "active": {
                      "type": "boolean"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                  "code": "BM-PAY-DAILY",
                  "name": "Pagamentos di\u00e1rios por PSP e m\u00e9todo",
                  "source": "payments",
                  "fields": "[\"date\",\"psp_id\",\"method\",\"count\",\"amount\",\"fees\"]",
                  "group_by": "[\"date\",\"psp_id\",\"method\"]",
                  "period": "daily",
                  "format": "csv",
                  "active": true,
                  "version": 2,
                  "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                  "created_at": "2026-03-02T09:15:00Z",
                  "updated_at": "2026-06-18T14:40:12Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/templates/{id}/run": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "runRegulatoryTemplate",
        "summary": "Generate a report now",
        "description": "For the period [from, to): the report is signed, stored write-once, and its totals checked against the ledger for the same period (payments: amount against the charge journals; ledger: debits against credits). A mismatch blocks submission (status blocked). Daily and monthly templates also run on their own for yesterday and last month (`regulatory_report.generated`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "to"
                ],
                "properties": {
                  "from": {
                    "type": "string",
                    "format": "date"
                  },
                  "to": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "example": {
                "from": "2026-10-31",
                "to": "2026-10-31"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The run.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "template_id": {
                      "type": "string"
                    },
                    "template_version": {
                      "type": "integer"
                    },
                    "code": {
                      "type": "string"
                    },
                    "period_start": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "period_end": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "trigger": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "reconciled": {
                      "type": "boolean"
                    },
                    "rows": {
                      "type": "integer"
                    },
                    "totals": {
                      "type": "string"
                    },
                    "ledger_totals": {
                      "type": "string"
                    },
                    "format": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "signature": {
                      "type": "string"
                    },
                    "signing_key_id": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KLkM29zNWk8XOFM5SgHNsugPV9",
                  "template_id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                  "template_version": 2,
                  "code": "BM-PAY-DAILY",
                  "period_start": "2026-10-10T00:00:00Z",
                  "period_end": "2026-10-11T00:00:00Z",
                  "trigger": "on_demand",
                  "status": "generated",
                  "reconciled": true,
                  "rows": 6,
                  "totals": "{\"MZN\":125000000}",
                  "ledger_totals": "{\"MZN\":125000000}",
                  "format": "csv",
                  "file_id": "3KFmKhlYELQZGQLw3WZICSjn9u0",
                  "sha256": "15e20481e573d63f895dc0d253eb93cf04e61bc4a07b151c3d57e7564f5b7c5c",
                  "signature": "MEUCIQDx3v\u2026",
                  "signing_key_id": "reg-signing-2026",
                  "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                  "created_at": "2026-10-11T07:30:04Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/runs": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listRegulatoryRuns",
        "summary": "Generated regulatory reports",
        "description": "Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "template_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "template_id": {
                            "type": "string"
                          },
                          "template_version": {
                            "type": "integer"
                          },
                          "code": {
                            "type": "string"
                          },
                          "period_start": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "period_end": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "trigger": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "reconciled": {
                            "type": "boolean"
                          },
                          "rows": {
                            "type": "integer"
                          },
                          "totals": {
                            "type": "string"
                          },
                          "ledger_totals": {
                            "type": "string"
                          },
                          "format": {
                            "type": "string"
                          },
                          "file_id": {
                            "type": "string"
                          },
                          "sha256": {
                            "type": "string"
                          },
                          "signature": {
                            "type": "string"
                          },
                          "signing_key_id": {
                            "type": "string"
                          },
                          "submitted_by": {
                            "type": "string"
                          },
                          "submitted_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KLkM29zNWk8XOFM5SgHNsugPV9",
                      "template_id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                      "template_version": 2,
                      "code": "BM-PAY-DAILY",
                      "period_start": "2026-10-10T00:00:00Z",
                      "period_end": "2026-10-11T00:00:00Z",
                      "trigger": "scheduled",
                      "status": "submitted",
                      "reconciled": true,
                      "rows": 6,
                      "totals": "{\"MZN\":125000000}",
                      "ledger_totals": "{\"MZN\":125000000}",
                      "format": "csv",
                      "file_id": "3KFmKhlYELQZGQLw3WZICSjn9u0",
                      "sha256": "15e20481e573d63f895dc0d253eb93cf04e61bc4a07b151c3d57e7564f5b7c5c",
                      "signature": "MEUCIQDx3v\u2026",
                      "signing_key_id": "reg-signing-2026",
                      "submitted_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                      "submitted_at": "2026-10-11T09:02:47Z",
                      "created_at": "2026-10-11T01:00:03Z"
                    },
                    {
                      "id": "3KhLW2YPbDu1SMgSpNcE9FdzNh0",
                      "template_id": "3KIaajCSOfi46dTWod2FEl5eSMW",
                      "template_version": 2,
                      "code": "BM-PAY-DAILY",
                      "period_start": "2026-10-09T00:00:00Z",
                      "period_end": "2026-10-10T00:00:00Z",
                      "trigger": "scheduled",
                      "status": "blocked",
                      "reconciled": false,
                      "rows": 5,
                      "totals": "{\"MZN\":98750000}",
                      "ledger_totals": "{\"MZN\":98625000}",
                      "detail": "totals do not reconcile to the ledger: MZN: report 98750000, ledger 98625000",
                      "format": "csv",
                      "file_id": "3KRlP4WSt8uUE2E2OYdHrWSK4QF",
                      "sha256": "8b1a9953c4611296a827abf8c47804d7e6c49c6b2f1c0e5a3d4f7b9e2a6c1d30",
                      "signature": "MEQCIF9a2k\u2026",
                      "signing_key_id": "reg-signing-2026",
                      "created_at": "2026-10-10T01:00:02Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/runs/{id}/file": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "regulatoryRunFile",
        "summary": "A report's signed file",
        "description": "With `X-Signature` and `X-Signing-Key-ID`. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              },
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              },
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/runs/{id}/submit": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "submitRegulatoryRun",
        "summary": "Submit a report (maker)",
        "description": "Submitting a reconciled report to Banco de Mo\u00e7ambique takes two people (F3-7): this prepares a `regulatory.submit` approval with the channel (portal, xml_upload, email, sftp, hand_delivery) and the reference the submission got; a different person approves it (POST /v1/admin/approvals/{id}/decide) and the run becomes `submitted` (`regulatory_report.submitted`), kept until `retain_until`. A blocked run, one already submitted or one waiting is 409 `not_submittable`. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Prepared; a second person must approve it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KZk0Ab2Cd4Ef6Gh8Ij0Kl2Mn4O",
                    "kind": "regulatory.submit",
                    "subject_id": "3KLkM29zNWk8XOFM5SgHNsugPV9",
                    "tenant_id": "platform",
                    "reason": "submit BM-PAY-DAILY for 2026-10-10 to 2026-10-11 by portal: monthly payments report",
                    "prepared_by": "key_ops_ana",
                    "status": "pending",
                    "created_at": "2026-10-12T08:00:00Z"
                  },
                  "message": "submission prepared; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "404": {
            "description": "No such run.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "not found",
                  "error": {
                    "code": "not_found",
                    "message": "not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`not_submittable`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_submittable",
                  "detail": "blocked: MZN: report 17500, ledger 15000",
                  "error": {
                    "code": "not_submittable",
                    "message": "blocked: MZN: report 17500, ledger 15000"
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "channel",
                  "reason"
                ],
                "properties": {
                  "channel": {
                    "type": "string",
                    "enum": [
                      "portal",
                      "xml_upload",
                      "email",
                      "sftp",
                      "hand_delivery"
                    ]
                  },
                  "reference": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "channel": "portal",
                "reference": "BDM-2026-10-0042",
                "reason": "monthly payments report"
              }
            }
          }
        }
      }
    },
    "/v1/admin/data-transfers": {
      "get": {
        "tags": [
          "Connectors"
        ],
        "operationId": "listDataTransfers",
        "summary": "The cross-border transfer register",
        "description": "One entry per acquirer outside the region (PSP-12.3): legal entity, country, the fields its allow-list permits and the fields sent so far, transfers and fields dropped, legal basis, data processing agreement, last and next review. Every outbound payload to such a connector carries only its allowed fields; any other field is dropped before the call (`data_transfer.blocked_field`) and each call records `data_transfer.sent`. A transfer that cannot be recorded is not sent. Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "connector": {
                            "type": "string"
                          },
                          "legal_entity": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "fields_allowed": {
                            "type": "string"
                          },
                          "fields_sent": {
                            "type": "string"
                          },
                          "legal_basis": {
                            "type": "string"
                          },
                          "dpa_reference": {
                            "type": "string"
                          },
                          "transfers": {
                            "type": "integer"
                          },
                          "blocked_fields": {
                            "type": "integer"
                          },
                          "last_transfer_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "reviewed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "reviewed_by": {
                            "type": "string"
                          },
                          "next_review_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K3cStcRGSc7g3n7YaS9mOyEJ7L",
                      "connector": "stripe",
                      "legal_entity": "Stripe Payments Europe, Limited",
                      "country": "IE",
                      "fields_allowed": "[\"amount\",\"currency\",\"metadata.billing_country\",\"metadata.email\",\"metadata.kulpay_challenge\",\"metadata.merchant_reference\",\"metadata.order_id\",\"payment_id\",\"return_url\",\"source.card.token\"]",
                      "fields_sent": "[\"amount\",\"currency\",\"metadata.merchant_reference\",\"payment_id\",\"return_url\",\"source.card.token\"]",
                      "legal_basis": "Contract performance (Lei n.\u00ba 3/2017, art. 59) with standard contractual clauses",
                      "dpa_reference": "DPA-STRIPE-2026-01",
                      "transfers": 18342,
                      "blocked_fields": 2210,
                      "last_transfer_at": "2026-10-11T10:59:12Z",
                      "reviewed_at": "2026-03-15T09:00:00Z",
                      "reviewed_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "next_review_at": "2027-03-15T09:00:00Z",
                      "created_at": "2026-01-20T08:00:00Z",
                      "updated_at": "2026-10-11T10:59:12Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/data-transfers/{connector}": {
      "patch": {
        "tags": [
          "Connectors"
        ],
        "operationId": "updateDataTransfer",
        "summary": "Set the legal basis or record the yearly review",
        "description": "Sets `legal_basis` and `dpa_reference`; `reviewed: true` records the yearly review (both must be set first) and moves the next one a year on. Each entry is announced by `data_transfer.register.review_due` when its review falls due. Records `config.data_transfer_register.saved`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "connector",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "legal_basis": {
                    "type": "string"
                  },
                  "dpa_reference": {
                    "type": "string"
                  },
                  "reviewed": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "legal_basis": "string",
                "dpa_reference": "2003014567",
                "reviewed": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "connector": {
                      "type": "string"
                    },
                    "legal_entity": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "fields_allowed": {
                      "type": "string"
                    },
                    "fields_sent": {
                      "type": "string"
                    },
                    "legal_basis": {
                      "type": "string"
                    },
                    "dpa_reference": {
                      "type": "string"
                    },
                    "transfers": {
                      "type": "integer"
                    },
                    "blocked_fields": {
                      "type": "integer"
                    },
                    "last_transfer_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "reviewed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "reviewed_by": {
                      "type": "string"
                    },
                    "next_review_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K3cStcRGSc7g3n7YaS9mOyEJ7L",
                  "connector": "stripe",
                  "legal_entity": "Stripe Payments Europe, Limited",
                  "country": "IE",
                  "fields_allowed": "[\"amount\",\"currency\",\"metadata.billing_country\",\"metadata.email\",\"metadata.kulpay_challenge\",\"metadata.merchant_reference\",\"metadata.order_id\",\"payment_id\",\"return_url\",\"source.card.token\"]",
                  "fields_sent": "[\"amount\",\"currency\",\"metadata.merchant_reference\",\"payment_id\",\"return_url\",\"source.card.token\"]",
                  "legal_basis": "Contract performance (Lei n.\u00ba 3/2017, art. 59) with standard contractual clauses",
                  "dpa_reference": "DPA-STRIPE-2026-01",
                  "transfers": 18342,
                  "blocked_fields": 2210,
                  "last_transfer_at": "2026-10-11T10:59:12Z",
                  "reviewed_at": "2026-10-11T11:10:00Z",
                  "reviewed_by": "3KIhzpLdNupTuLgu8bwZOQspar2",
                  "next_review_at": "2027-10-11T11:10:00Z",
                  "created_at": "2026-01-20T08:00:00Z",
                  "updated_at": "2026-10-11T11:10:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Review recorded without a legal basis or DPA.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "set the legal basis and the data processing agreement before recording the review",
                  "error": {
                    "code": "invalid_request",
                    "message": "set the legal basis and the data processing agreement before recording the review"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No entry for that connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no register entry for that connector",
                  "error": {
                    "code": "not_found",
                    "message": "no register entry for that connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/data-transfers/export": {
      "get": {
        "tags": [
          "Connectors"
        ],
        "operationId": "exportDataTransfers",
        "summary": "Export the register for regulators",
        "description": "The register as CSV, signed (`X-Signature`, `X-Signing-Key-ID`) when a report signing key is configured. Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "text/csv.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "description": "The cross-border transfer register as CSV (data-transfer-register.csv). When a signing key is configured, X-Signature and X-Signing-Key-ID carry its signature."
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/kulpay-apps": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "appLinks",
        "summary": "Where KulPay's apps live",
        "security": [],
        "description": "The configured addresses of the docs, API reference, PSP onboarding, playground and (when the deployment names one) operator dashboard, so a static frontend can link to them. Unset addresses are omitted. No authentication.",
        "responses": {
          "200": {
            "description": "`{docs, api_reference, onboarding, playground, dashboard}`, each optional.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "docs": {
                      "type": "string"
                    },
                    "api_reference": {
                      "type": "string"
                    },
                    "onboarding": {
                      "type": "string"
                    },
                    "portal": {
                      "type": "string"
                    },
                    "psps": {
                      "type": "string"
                    },
                    "lsps": {
                      "type": "string"
                    },
                    "onboarding_lsp": {
                      "type": "string"
                    },
                    "playground": {
                      "type": "string"
                    },
                    "dashboard": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "docs": "https://pay.example/docs",
                  "api_reference": "https://pay.example/docs/api",
                  "onboarding": "https://pay.example/onboarding",
                  "portal": "https://pay.example/portal",
                  "psps": "https://pay.example/psps",
                  "lsps": "https://pay.example/lsp",
                  "onboarding_lsp": "https://pay.example/onboarding?kind=lsp",
                  "playground": "http://play.local",
                  "dashboard": "http://ops.internal/admin"
                }
              }
            }
          }
        }
      }
    },
    "/v1/cash-deposits/intents": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createDepositIntent",
        "summary": "Initiate a cash deposit from a reference lookup",
        "description": "Step 2 of a partner cash deposit (PSP-7.8). After `POST /v1/name-lookups` with `method: \"cash_deposit\"` and a `reference`, register the deposit against the returned `lookup_id` before taking the cash: the beneficiary, amount and currency are fixed, and the amount must respect the lookup's constraints. One lookup initiates one deposit (a repeat returns it); a lookup expires after at most 15 minutes, an intent after 24 hours if not confirmed (`cash_in.initiation_expired`).\n\n`depositor_metadata` and `beneficiary_metadata` carry whatever the partner needs (string pairs, at most 50 keys, keys up to 40 and values up to 500 characters), returned on the intent and never put in an event (only their keys are). A `depositor_metadata.email` is told when the deposit is credited. Records `cash_in.initiated`. Requires `transactions:write` and an `Idempotency-Key`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lookup_id",
                  "amount",
                  "currency"
                ],
                "properties": {
                  "lookup_id": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "string",
                    "example": "1500.00"
                  },
                  "currency": {
                    "type": "string",
                    "example": "MZN"
                  },
                  "partner_reference": {
                    "type": "string",
                    "description": "The partner's own reference for the deposit."
                  },
                  "depositor_metadata": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50
                  },
                  "beneficiary_metadata": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 500
                    },
                    "maxProperties": 50
                  }
                }
              },
              "example": {
                "lookup_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "amount": "1500.00",
                "currency": "MZN",
                "partner_reference": "2003014567",
                "depositor_metadata": {
                  "key": "1250.00"
                },
                "beneficiary_metadata": {
                  "key": "1250.00"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The intent (status `initiated`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "lookup_id": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "depositor_metadata": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        }
                      }
                    },
                    "beneficiary_metadata": {
                      "type": "object",
                      "properties": {
                        "invoice": {
                          "type": "string"
                        }
                      }
                    },
                    "destination_kind": {
                      "type": "string",
                      "enum": [
                        "wallet",
                        "kulpay_invoice",
                        "kulpay_bill"
                      ],
                      "description": "Where the deposit goes, as the resolver decided (IO-1.3)."
                    },
                    "charge_payer": {
                      "type": "string",
                      "enum": [
                        "customer",
                        "kuloffice",
                        "biller"
                      ]
                    },
                    "charge_minor": {
                      "type": "integer",
                      "description": "The charge on the deposit, in minor units."
                    },
                    "settlement_mode": {
                      "type": "string",
                      "enum": [
                        "immediate",
                        "batch"
                      ]
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "What the depositor pays: the amount plus the charge when the customer pays it. The bank statement must show this amount."
                    },
                    "outcome": {
                      "type": "string",
                      "enum": [
                        "processing",
                        "credited",
                        "returned",
                        "failed"
                      ],
                      "description": "Where the confirmed deposit stands: processing until terminal (credited, returned, failed); cash_in.completed tells the partner."
                    }
                  }
                },
                "example": {
                  "id": "3KCZf1Z3amItUf8AcdAyGBLH0Pu",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "lookup_id": "3K74gnbXRGdhjvd7xwAFB8mAGoF",
                  "reference": "800123456",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "partner_reference": "AG-MPT-000981",
                  "mode": "live",
                  "status": "initiated",
                  "expires_at": "2026-10-12T10:50:00Z",
                  "created_at": "2026-10-11T10:50:00Z",
                  "updated_at": "2026-10-11T10:50:00Z",
                  "depositor_metadata": {
                    "name": "Ana Macu\u00e1cua",
                    "phone": "+258841234567",
                    "email": "ana.macuacua@example.co.mz"
                  },
                  "beneficiary_metadata": {
                    "invoice": "FT2026/10/0042"
                  }
                }
              }
            }
          },
          "403": {
            "description": "`not_a_partner`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "not_a_partner",
                  "detail": "this key's account is not a partner bank or agent",
                  "error": {
                    "code": "not_a_partner",
                    "message": "this key's account is not a partner bank or agent"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`deposit_refused`: unknown or expired lookup, currency or amount outside the reference's limits, invalid metadata. `pilot_limit` while the account is a PSP in pilot: the live transaction breaks the pilot setting named in `rule` (`per_transaction`, `daily`, `monthly`, `currency`, `settings_missing`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "deposit_refused",
                  "detail": "the lookup has expired: look the reference up again",
                  "error": {
                    "code": "deposit_refused",
                    "message": "the lookup has expired: look the reference up again"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listDepositIntents",
        "summary": "Your deposit intents",
        "description": "Newest first, optionally `?status=initiated|reported|expired`. Requires `transactions:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "lookup_id": {
                            "type": "string"
                          },
                          "reference": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "partner_reference": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "deposit_id": {
                            "type": "string"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "depositor_metadata": {
                            "type": "object",
                            "properties": {
                              "name": {
                                "type": "string"
                              },
                              "phone": {
                                "type": "string"
                              }
                            }
                          },
                          "beneficiary_metadata": {
                            "type": "object",
                            "properties": {}
                          },
                          "destination_kind": {
                            "type": "string",
                            "enum": [
                              "wallet",
                              "kulpay_invoice",
                              "kulpay_bill"
                            ],
                            "description": "Where the deposit goes, as the resolver decided (IO-1.3)."
                          },
                          "charge_payer": {
                            "type": "string",
                            "enum": [
                              "customer",
                              "kuloffice",
                              "biller"
                            ]
                          },
                          "charge_minor": {
                            "type": "integer",
                            "description": "The charge on the deposit, in minor units."
                          },
                          "settlement_mode": {
                            "type": "string",
                            "enum": [
                              "immediate",
                              "batch"
                            ]
                          },
                          "total_minor": {
                            "type": "integer",
                            "description": "What the depositor pays: the amount plus the charge when the customer pays it. The bank statement must show this amount."
                          },
                          "outcome": {
                            "type": "string",
                            "enum": [
                              "processing",
                              "credited",
                              "returned",
                              "failed"
                            ],
                            "description": "Where the confirmed deposit stands: processing until terminal (credited, returned, failed); cash_in.completed tells the partner."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KCZf1Z3amItUf8AcdAyGBLH0Pu",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "lookup_id": "3K74gnbXRGdhjvd7xwAFB8mAGoF",
                      "reference": "800123456",
                      "amount_minor": 125000,
                      "currency": "MZN",
                      "partner_reference": "AG-MPT-000981",
                      "mode": "live",
                      "status": "reported",
                      "deposit_id": "3KVioCC5V4yauYhsd56OlrZgzWl",
                      "expires_at": "2026-10-12T10:50:00Z",
                      "created_at": "2026-10-11T10:50:00Z",
                      "updated_at": "2026-10-11T10:58:31Z",
                      "depositor_metadata": {
                        "name": "Ana Macu\u00e1cua",
                        "phone": "+258841234567"
                      },
                      "beneficiary_metadata": {}
                    },
                    {
                      "id": "3Kt4lT89R6ASddnqPMzC0MLmKOI",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "lookup_id": "3Kp8uNxsD8YF5tP5OXNHBKaZf8M",
                      "reference": "800987654",
                      "amount_minor": 450000,
                      "currency": "MZN",
                      "mode": "live",
                      "status": "initiated",
                      "expires_at": "2026-10-12T11:05:00Z",
                      "created_at": "2026-10-11T11:05:00Z",
                      "updated_at": "2026-10-11T11:05:00Z",
                      "depositor_metadata": {},
                      "beneficiary_metadata": {}
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/cash-deposits/intents/{id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getDepositIntent",
        "summary": "A deposit intent",
        "description": "Requires `transactions:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The intent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "lookup_id": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "depositor_metadata": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        }
                      }
                    },
                    "beneficiary_metadata": {
                      "type": "object",
                      "properties": {
                        "invoice": {
                          "type": "string"
                        }
                      }
                    },
                    "bill": {
                      "type": "object",
                      "description": "The bill the deposit paid, for a deposit to an invoice or a biller's product (IO-2.5, QA T-302): the bill payment, its signed receipt and its artifacts, tokens and PINs masked as on the receipt, whose link reveals them to the phone given in depositor_metadata.phone (texted the token when given).",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "paying",
                            "paid",
                            "failed"
                          ]
                        },
                        "payment_id": {
                          "type": "string"
                        },
                        "payment_status": {
                          "type": "string"
                        },
                        "biller": {
                          "type": "string"
                        },
                        "product": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "string"
                        },
                        "payer_fee": {
                          "type": "string"
                        },
                        "total": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "receipt_url": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "artifacts": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "breakdown": {
                          "type": "object"
                        },
                        "token_texted": {
                          "type": "boolean"
                        }
                      }
                    },
                    "destination_kind": {
                      "type": "string",
                      "enum": [
                        "wallet",
                        "kulpay_invoice",
                        "kulpay_bill"
                      ],
                      "description": "Where the deposit goes, as the resolver decided (IO-1.3)."
                    },
                    "charge_payer": {
                      "type": "string",
                      "enum": [
                        "customer",
                        "kuloffice",
                        "biller"
                      ]
                    },
                    "charge_minor": {
                      "type": "integer",
                      "description": "The charge on the deposit, in minor units."
                    },
                    "settlement_mode": {
                      "type": "string",
                      "enum": [
                        "immediate",
                        "batch"
                      ]
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "What the depositor pays: the amount plus the charge when the customer pays it. The bank statement must show this amount."
                    },
                    "outcome": {
                      "type": "string",
                      "enum": [
                        "processing",
                        "credited",
                        "returned",
                        "failed"
                      ],
                      "description": "Where the confirmed deposit stands: processing until terminal (credited, returned, failed); cash_in.completed tells the partner."
                    }
                  }
                },
                "example": {
                  "id": "3KCZf1Z3amItUf8AcdAyGBLH0Pu",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "lookup_id": "3K74gnbXRGdhjvd7xwAFB8mAGoF",
                  "reference": "800123456",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "partner_reference": "AG-MPT-000981",
                  "mode": "live",
                  "status": "initiated",
                  "expires_at": "2026-10-12T10:50:00Z",
                  "created_at": "2026-10-11T10:50:00Z",
                  "updated_at": "2026-10-11T10:50:00Z",
                  "depositor_metadata": {
                    "name": "Ana Macu\u00e1cua",
                    "phone": "+258841234567"
                  },
                  "beneficiary_metadata": {
                    "invoice": "FT2026/10/0042"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such deposit intent",
                  "error": {
                    "code": "not_found",
                    "message": "no such deposit intent"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/cash-deposits/intents/{id}/confirm": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "confirmDepositIntent",
        "summary": "Confirm an initiated cash deposit",
        "description": "Step 3 (PSP-7.9): the receiving bank `account`, the statement `entry_id` and `value_date`. The deposit is reported (PSP-7.6) for the intent's amount, currency and beneficiary, and credited only when the receiving account's statement shows the same entry and amount; an unknown beneficiary goes to suspense. Confirming again with the same entry returns the same deposit. Records `cash_in.initiation_confirmed` and `cash_in.reported`. Requires `transactions:write` and an `Idempotency-Key`. What credits the beneficiary is the PSP's choice (its CASH_IN capability row's `credit_on`): this confirmation, by default, the statement then reconciling it (`cash_in.reconciled`, or `cash_in.exception` for an operator), or the statement line. A credit to a wallet is applied at its resolver at once; the deposit is final for the partner only when its `outcome` is terminal (`credited`, `returned`, `failed`): a rejected credit goes to suspense and waits for an operator. `cash_in.completed` tells the partner.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account",
                  "entry_id",
                  "value_date"
                ],
                "properties": {
                  "account": {
                    "type": "string"
                  },
                  "entry_id": {
                    "type": "string"
                  },
                  "value_date": {
                    "type": "string",
                    "format": "date"
                  }
                }
              },
              "example": {
                "account": "2003014567",
                "entry_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "value_date": "2026-10-31"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{intent, deposit}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "intent": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "lookup_id": {
                          "type": "string"
                        },
                        "reference": {
                          "type": "string"
                        },
                        "amount_minor": {
                          "type": "integer"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "partner_reference": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "deposit_id": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "depositor_metadata": {
                          "type": "object",
                          "properties": {
                            "name": {
                              "type": "string"
                            },
                            "phone": {
                              "type": "string"
                            }
                          }
                        },
                        "beneficiary_metadata": {
                          "type": "object",
                          "properties": {
                            "invoice": {
                              "type": "string"
                            }
                          }
                        },
                        "destination_kind": {
                          "type": "string",
                          "enum": [
                            "wallet",
                            "kulpay_invoice",
                            "kulpay_bill"
                          ],
                          "description": "Where the deposit goes, as the resolver decided (IO-1.3)."
                        },
                        "charge_payer": {
                          "type": "string",
                          "enum": [
                            "customer",
                            "kuloffice",
                            "biller"
                          ]
                        },
                        "charge_minor": {
                          "type": "integer",
                          "description": "The charge on the deposit, in minor units."
                        },
                        "settlement_mode": {
                          "type": "string",
                          "enum": [
                            "immediate",
                            "batch"
                          ]
                        },
                        "total_minor": {
                          "type": "integer",
                          "description": "What the depositor pays: the amount plus the charge when the customer pays it. The bank statement must show this amount."
                        },
                        "outcome": {
                          "type": "string",
                          "enum": [
                            "processing",
                            "credited",
                            "returned",
                            "failed"
                          ],
                          "description": "Where the confirmed deposit stands: processing until terminal (credited, returned, failed); cash_in.completed tells the partner."
                        }
                      }
                    },
                    "deposit": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "psp_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "account": {
                          "type": "string"
                        },
                        "entry_id": {
                          "type": "string"
                        },
                        "value_date": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "amount_minor": {
                          "type": "integer"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "depositor_ref": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "deadline_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "reported_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "intent": {
                    "id": "3KCZf1Z3amItUf8AcdAyGBLH0Pu",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "lookup_id": "3K74gnbXRGdhjvd7xwAFB8mAGoF",
                    "reference": "800123456",
                    "amount_minor": 125000,
                    "currency": "MZN",
                    "partner_reference": "AG-MPT-000981",
                    "mode": "live",
                    "status": "reported",
                    "deposit_id": "3KVioCC5V4yauYhsd56OlrZgzWl",
                    "expires_at": "2026-10-12T10:50:00Z",
                    "created_at": "2026-10-11T10:50:00Z",
                    "updated_at": "2026-10-11T10:58:31Z",
                    "depositor_metadata": {
                      "name": "Ana Macu\u00e1cua",
                      "phone": "+258841234567"
                    },
                    "beneficiary_metadata": {
                      "invoice": "FT2026/10/0042"
                    }
                  },
                  "deposit": {
                    "id": "3KVioCC5V4yauYhsd56OlrZgzWl",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "tenant_id": "3K12JLy9OnIwuN9yzoIgIKQpHJX",
                    "account": "000800001234567890157",
                    "entry_id": "BCI-20261011-000731",
                    "value_date": "2026-10-11T00:00:00Z",
                    "amount_minor": 125000,
                    "currency": "MZN",
                    "depositor_ref": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                    "mode": "live",
                    "status": "reported",
                    "deadline_at": "2026-10-13T10:58:31Z",
                    "reported_at": "2026-10-11T10:58:31Z",
                    "updated_at": "2026-10-11T10:58:31Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such deposit intent",
                  "error": {
                    "code": "not_found",
                    "message": "no such deposit intent"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`deposit_refused`: expired, or already confirmed with another entry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "deposit_refused",
                  "detail": "this deposit intent has expired",
                  "error": {
                    "code": "deposit_refused",
                    "message": "this deposit intent has expired"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/billers": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listBillers",
        "summary": "Billers and products you may sell",
        "description": "The active billers with the enabled products open to your account (BIL-1.3): amount rules, the customer reference to ask for, whether a lookup is required, the fee the payer pays, and offers (bundles, bouquets). Filter by `category` and `q` (name or code). Responses carry an `ETag` and `Cache-Control: max-age=300`; send `If-None-Match` to revalidate. Requires `bills:read`.",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "electricity",
                "water",
                "telecom",
                "tv",
                "internet",
                "education",
                "insurance",
                "government",
                "other"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "example": "EDM"
                          },
                          "name": {
                            "type": "string"
                          },
                          "category": {
                            "type": "string",
                            "enum": [
                              "electricity",
                              "water",
                              "telecom",
                              "tv",
                              "internet",
                              "education",
                              "insurance",
                              "government",
                              "other"
                            ]
                          },
                          "country": {
                            "type": "string",
                            "example": "MZ"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "support_email": {
                            "type": "string"
                          },
                          "support_phone": {
                            "type": "string"
                          },
                          "products": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "code": {
                                  "type": "string",
                                  "example": "CREDELEC"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "shape": {
                                  "type": "string",
                                  "enum": [
                                    "metered_prepaid",
                                    "account_topup",
                                    "airtime",
                                    "postpaid",
                                    "voucher"
                                  ]
                                },
                                "currency": {
                                  "type": "string",
                                  "example": "MZN"
                                },
                                "amount_rule": {
                                  "type": "string",
                                  "enum": [
                                    "range",
                                    "fixed",
                                    "denominations",
                                    "presented"
                                  ]
                                },
                                "min": {
                                  "type": "string",
                                  "example": "1500.00"
                                },
                                "max": {
                                  "type": "string",
                                  "example": "1500.00"
                                },
                                "fixed": {
                                  "type": "string",
                                  "example": "1500.00"
                                },
                                "denominations": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "example": "1500.00"
                                  }
                                },
                                "customer_reference": {
                                  "type": "object",
                                  "properties": {
                                    "label": {
                                      "type": "string",
                                      "example": "N\u00famero do contador"
                                    },
                                    "pattern": {
                                      "type": "string",
                                      "example": "^\\d{11}$"
                                    },
                                    "example": {
                                      "type": "string"
                                    }
                                  }
                                },
                                "lookup_required": {
                                  "type": "boolean"
                                },
                                "fee": {
                                  "type": "object",
                                  "description": "The fee as the payer sees it. With `bearer: biller` the payer pays no fee and no amounts are shown.",
                                  "properties": {
                                    "bearer": {
                                      "type": "string",
                                      "enum": [
                                        "payer",
                                        "biller"
                                      ]
                                    },
                                    "fixed": {
                                      "type": "string",
                                      "example": "1500.00"
                                    },
                                    "percent": {
                                      "type": "string",
                                      "example": "1.5"
                                    },
                                    "min": {
                                      "type": "string",
                                      "example": "1500.00"
                                    },
                                    "max": {
                                      "type": "string",
                                      "example": "1500.00"
                                    },
                                    "currency": {
                                      "type": "string"
                                    }
                                  }
                                },
                                "offers": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "code": {
                                        "type": "string"
                                      },
                                      "name": {
                                        "type": "string"
                                      },
                                      "price": {
                                        "type": "string",
                                        "example": "1500.00"
                                      },
                                      "validity": {
                                        "type": "string",
                                        "example": "30d"
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "code": "TVCABO",
                      "name": "TV Cabo",
                      "category": "tv",
                      "country": "MZ",
                      "support_phone": "+258 21 480 500",
                      "products": [
                        {
                          "code": "PACOTE",
                          "name": "Pacote",
                          "shape": "account_topup",
                          "currency": "MZN",
                          "amount_rule": "fixed",
                          "fixed": "750.00",
                          "customer_reference": {
                            "label": "Smartcard",
                            "pattern": "^\\d{10}$"
                          },
                          "lookup_required": false,
                          "fee": {
                            "bearer": "payer",
                            "fixed": "5.00",
                            "currency": "MZN"
                          },
                          "offers": [
                            {
                              "code": "TVBASIC",
                              "name": "Pacote B\u00e1sico",
                              "price": "750.00",
                              "validity": "30d"
                            },
                            {
                              "code": "TVPREMIUM",
                              "name": "Pacote Premium",
                              "price": "2500.00",
                              "validity": "30d"
                            }
                          ]
                        }
                      ]
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          }
        }
      }
    },
    "/v1/billers/{code}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBiller",
        "summary": "One biller you may sell",
        "description": "As in the listing. Requires `bills:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          }
        ],
        "responses": {
          "200": {
            "description": "The biller.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "EDM"
                    },
                    "name": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "electricity",
                        "water",
                        "telecom",
                        "tv",
                        "internet",
                        "education",
                        "insurance",
                        "government",
                        "other"
                      ]
                    },
                    "country": {
                      "type": "string",
                      "example": "MZ"
                    },
                    "logo_url": {
                      "type": "string"
                    },
                    "support_email": {
                      "type": "string"
                    },
                    "support_phone": {
                      "type": "string"
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string",
                            "example": "CREDELEC"
                          },
                          "name": {
                            "type": "string"
                          },
                          "shape": {
                            "type": "string",
                            "enum": [
                              "metered_prepaid",
                              "account_topup",
                              "airtime",
                              "postpaid",
                              "voucher"
                            ]
                          },
                          "currency": {
                            "type": "string",
                            "example": "MZN"
                          },
                          "amount_rule": {
                            "type": "string",
                            "enum": [
                              "range",
                              "fixed",
                              "denominations",
                              "presented"
                            ]
                          },
                          "min": {
                            "type": "string",
                            "example": "1500.00"
                          },
                          "max": {
                            "type": "string",
                            "example": "1500.00"
                          },
                          "fixed": {
                            "type": "string",
                            "example": "1500.00"
                          },
                          "denominations": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "example": "1500.00"
                            }
                          },
                          "customer_reference": {
                            "type": "object",
                            "properties": {
                              "label": {
                                "type": "string",
                                "example": "N\u00famero do contador"
                              },
                              "pattern": {
                                "type": "string",
                                "example": "^\\d{11}$"
                              },
                              "example": {
                                "type": "string"
                              }
                            }
                          },
                          "lookup_required": {
                            "type": "boolean"
                          },
                          "fee": {
                            "type": "object",
                            "description": "The fee as the payer sees it. With `bearer: biller` the payer pays no fee and no amounts are shown.",
                            "properties": {
                              "bearer": {
                                "type": "string",
                                "enum": [
                                  "payer",
                                  "biller"
                                ]
                              },
                              "fixed": {
                                "type": "string",
                                "example": "1500.00"
                              },
                              "percent": {
                                "type": "string",
                                "example": "1.5"
                              },
                              "min": {
                                "type": "string",
                                "example": "1500.00"
                              },
                              "max": {
                                "type": "string",
                                "example": "1500.00"
                              },
                              "currency": {
                                "type": "string"
                              }
                            }
                          },
                          "offers": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "code": {
                                  "type": "string"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "price": {
                                  "type": "string",
                                  "example": "1500.00"
                                },
                                "validity": {
                                  "type": "string",
                                  "example": "30d"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "code": "TVCABO",
                  "name": "TV Cabo",
                  "category": "tv",
                  "country": "MZ",
                  "support_phone": "+258 21 480 500",
                  "products": [
                    {
                      "code": "PACOTE",
                      "name": "Pacote",
                      "shape": "account_topup",
                      "currency": "MZN",
                      "amount_rule": "fixed",
                      "fixed": "750.00",
                      "customer_reference": {
                        "label": "Smartcard",
                        "pattern": "^\\d{10}$"
                      },
                      "lookup_required": false,
                      "fee": {
                        "bearer": "payer",
                        "fixed": "5.00",
                        "currency": "MZN"
                      },
                      "offers": [
                        {
                          "code": "TVBASIC",
                          "name": "Pacote B\u00e1sico",
                          "price": "750.00",
                          "validity": "30d"
                        },
                        {
                          "code": "TVPREMIUM",
                          "name": "Pacote Premium",
                          "price": "2500.00",
                          "validity": "30d"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminListBillers",
        "summary": "Billers (operators)",
        "description": "Every biller, any status, filterable by `status`, `category` and `q`. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "suspended",
                "retired"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "category": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "support_email": {
                            "type": "string"
                          },
                          "support_phone": {
                            "type": "string"
                          },
                          "allow_partial": {
                            "type": "boolean"
                          },
                          "allow_overpay": {
                            "type": "boolean"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "presentment_modes": {
                            "type": "array",
                            "items": {}
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                      "code": "EDM",
                      "name": "Electricidade de Mo\u00e7ambique",
                      "category": "electricity",
                      "country": "MZ",
                      "logo_url": "https://cdn.example.co.mz/billers/edm.png",
                      "support_email": "apoio@example.co.mz",
                      "support_phone": "+258841234567",
                      "allow_partial": false,
                      "allow_overpay": false,
                      "status": "active",
                      "created_at": "2026-03-02T08:15:00Z",
                      "updated_at": "2026-09-18T10:42:11Z",
                      "presentment_modes": []
                    },
                    {
                      "id": "3KUXR1mVq8sLd2tPfGhZa7YwKe4",
                      "code": "AGUAS_MAPUTO",
                      "name": "\u00c1guas da Regi\u00e3o de Maputo",
                      "category": "water",
                      "country": "MZ",
                      "support_email": "clientes@example.co.mz",
                      "tenant_id": "3KUXS7nBw2cQe5rTgHjXb9ZvLf5",
                      "allow_partial": true,
                      "allow_overpay": false,
                      "invoice_expiry_days": 30,
                      "remit_bank": "BCI",
                      "remit_account": "000800001234567890157",
                      "remit_frequency": "daily",
                      "status": "active",
                      "created_at": "2026-04-11T09:00:00Z",
                      "updated_at": "2026-10-01T14:20:37Z",
                      "presentment_modes": [
                        "push",
                        "lookup"
                      ]
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "createBiller",
        "summary": "Register a biller",
        "description": "Creates a biller as a `draft` (BIL-1.1); channels see it once a second operator activates it. Records `biller.created`. Requires `connectors:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^[A-Z0-9_]{2,20}$"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "electricity",
                      "water",
                      "telecom",
                      "tv",
                      "internet",
                      "education",
                      "insurance",
                      "government",
                      "other"
                    ]
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "logo_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "support_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "support_phone": {
                    "type": "string"
                  },
                  "tenant_id": {
                    "type": "string",
                    "description": "An onboarded organisation with the biller role, if any."
                  },
                  "settlement_mode": {
                    "type": "string",
                    "enum": [
                      "immediate",
                      "batch"
                    ],
                    "description": "Remitted per payment (immediate, within the minute) or in the daily batch (batch, the default), by the rail of its credit account (IO-4.3)."
                  }
                }
              },
              "example": {
                "code": "AGUAS",
                "name": "\u00c1guas",
                "category": "water",
                "country": "MZ",
                "tenant_id": "3KWPl3PNWBvbCepPhJBUlOXiLUx",
                "presentment_modes": [
                  "push"
                ],
                "invoice_expiry_days": 30
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The biller.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "allow_partial": {
                      "type": "boolean"
                    },
                    "allow_overpay": {
                      "type": "boolean"
                    },
                    "invoice_expiry_days": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "presentment_modes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPlFlovXTX5hJHsBDDV47dyHf",
                  "code": "AGUAS",
                  "name": "\u00c1guas",
                  "category": "water",
                  "country": "MZ",
                  "tenant_id": "3KWPl3PNWBvbCepPhJBUlOXiLUx",
                  "allow_partial": false,
                  "allow_overpay": false,
                  "invoice_expiry_days": 30,
                  "status": "draft",
                  "created_at": "2026-10-10T22:14:59.252334782Z",
                  "updated_at": "2026-10-10T22:14:59.252334782Z",
                  "presentment_modes": [
                    "push"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "category is one of electricity, water, telecom, tv, internet, education, insurance, government, other",
                  "error": {
                    "code": "invalid_request",
                    "message": "category is one of electricity, water, telecom, tv, internet, education, insurance, government, other"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminGetBiller",
        "summary": "A biller with its products",
        "description": "Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          }
        ],
        "responses": {
          "200": {
            "description": "`{biller, products}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "biller": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "category": {
                          "type": "string"
                        },
                        "country": {
                          "type": "string"
                        },
                        "support_phone": {
                          "type": "string"
                        },
                        "allow_partial": {
                          "type": "boolean"
                        },
                        "allow_overpay": {
                          "type": "boolean"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "presentment_modes": {
                          "type": "array",
                          "items": {}
                        }
                      }
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "allowed_channels": {},
                          "amount_rule": {
                            "type": "string"
                          },
                          "biller_id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "connectors": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "denominations": {},
                          "enabled": {
                            "type": "boolean"
                          },
                          "fee_bearer": {
                            "type": "string"
                          },
                          "fee_bps": {
                            "type": "integer"
                          },
                          "fee_fixed_minor": {
                            "type": "integer"
                          },
                          "fixed_minor": {
                            "type": "integer"
                          },
                          "id": {
                            "type": "string"
                          },
                          "lookup_required": {
                            "type": "boolean"
                          },
                          "model": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "reference_label": {
                            "type": "string"
                          },
                          "reference_pattern": {
                            "type": "string"
                          },
                          "shape": {
                            "type": "string"
                          },
                          "supplier_product": {
                            "type": "string"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "version": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "biller": {
                    "id": "3KWPkylzKmRsF6Rq3xef8pjun2x",
                    "code": "TVCABO",
                    "name": "TV Cabo",
                    "category": "tv",
                    "country": "MZ",
                    "support_phone": "+258 21 480 500",
                    "allow_partial": false,
                    "allow_overpay": false,
                    "status": "active",
                    "created_at": "2026-10-10T22:14:57.786577491Z",
                    "updated_at": "2026-10-10T22:14:57.920983259Z",
                    "presentment_modes": []
                  },
                  "products": [
                    {
                      "allowed_channels": null,
                      "amount_rule": "fixed",
                      "biller_id": "3KWPkylzKmRsF6Rq3xef8pjun2x",
                      "code": "PACOTE",
                      "connectors": [
                        "3KWPkweeFBr3p9OxYa5dTFjzMCF"
                      ],
                      "created_at": "2026-10-10T22:14:57.814553026Z",
                      "currency": "MZN",
                      "denominations": null,
                      "enabled": true,
                      "fee_bearer": "payer",
                      "fee_bps": 0,
                      "fee_fixed_minor": 500,
                      "fixed_minor": 75000,
                      "id": "3KWPkv1wLq7YHJt6Jqg48mMzOKS",
                      "lookup_required": false,
                      "model": "agent",
                      "name": "Pacote",
                      "reference_label": "Smartcard",
                      "reference_pattern": "^\\d{10}$",
                      "shape": "account_topup",
                      "supplier_product": "tv",
                      "updated_at": "2026-10-10T22:14:57.814553026Z",
                      "version": 1
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "updateBiller",
        "summary": "Change a biller's details",
        "description": "Fields left out keep their value; `remit_bank` and `remit_account` are set here only while the biller is a draft, then with PUT /v1/admin/billers/{code}/remit-account (four eyes); the status changes only through `/status`. Records `biller.updated`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^[A-Z0-9_]{2,20}$"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "electricity",
                      "water",
                      "telecom",
                      "tv",
                      "internet",
                      "education",
                      "insurance",
                      "government",
                      "other"
                    ]
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "logo_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "support_email": {
                    "type": "string",
                    "format": "email"
                  },
                  "support_phone": {
                    "type": "string"
                  },
                  "tenant_id": {
                    "type": "string",
                    "description": "An onboarded organisation with the biller role, if any."
                  },
                  "settlement_mode": {
                    "type": "string",
                    "enum": [
                      "immediate",
                      "batch"
                    ],
                    "description": "Remitted per payment (immediate, within the minute) or in the daily batch (batch, the default), by the rail of its credit account (IO-4.3)."
                  }
                }
              },
              "example": {
                "presentment_modes": [
                  "push",
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The biller.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string"
                    },
                    "tenant_id": {
                      "type": "string"
                    },
                    "allow_partial": {
                      "type": "boolean"
                    },
                    "allow_overpay": {
                      "type": "boolean"
                    },
                    "invoice_expiry_days": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "presentment_modes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPlFlovXTX5hJHsBDDV47dyHf",
                  "code": "AGUAS",
                  "name": "\u00c1guas",
                  "category": "water",
                  "country": "MZ",
                  "tenant_id": "3KWPl3PNWBvbCepPhJBUlOXiLUx",
                  "allow_partial": false,
                  "allow_overpay": false,
                  "invoice_expiry_days": 30,
                  "status": "active",
                  "created_at": "2026-10-10T22:14:59.252334782Z",
                  "updated_at": "2026-10-10T22:14:59.846639591Z",
                  "presentment_modes": [
                    "push",
                    "file"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "support_email is not an email address",
                  "error": {
                    "code": "invalid_request",
                    "message": "support_email is not an email address"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/status": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "requestBillerStatus",
        "summary": "Activate, suspend or retire a biller",
        "description": "Prepares the change for a second operator (maker-checker); approving it records `biller.status_changed`. Activation needs an enabled product with a supply connector. A suspended biller refuses new payments. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status",
                  "reason"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "suspended",
                      "retired"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "status": "active",
                "reason": "contract"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPkwtupluYYLGAw7At48TbMyG",
                    "kind": "bill.biller_status",
                    "subject_id": "3KWPkylzKmRsF6Rq3xef8pjun2x",
                    "tenant_id": "platform",
                    "payload": "{\"status\":\"active\"}",
                    "reason": "contract",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:14:57.89256229Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the biller needs an enabled product with a supply connector before it is activated",
                  "error": {
                    "code": "invalid_request",
                    "message": "the biller needs an enabled product with a supply connector before it is activated"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/certification": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "certifyBiller",
        "summary": "Certify a KulOffice biller's integration",
        "description": "For a biller onboarded in KulOffice (IO-3.5), in the sandbox: checks its gates (`product_ready`; `sandbox_payment_succeeded`, a test-mode payment of the biller that succeeded through its contract; `invoice_presented` when it presents invoices). All met, its technical status becomes `certified` (`biller.updated`, and `biller.integration_status` to KulOffice); it then goes live with `POST /v1/admin/billers/{code}/status` and a second operator. Calling it again is safe. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `AGUAS_DE_MAPUTO`."
          }
        ],
        "responses": {
          "200": {
            "description": "The certification: its gates and whether it passed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "biller_id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "technical_status": {
                      "type": "string",
                      "enum": [
                        "sandbox",
                        "certified"
                      ]
                    },
                    "certified": {
                      "type": "boolean"
                    },
                    "gates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "met": {
                            "type": "boolean"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "biller_id": "bil_123",
                  "code": "AGUAS_DE_MAPUTO",
                  "technical_status": "certified",
                  "certified": true,
                  "gates": [
                    {
                      "name": "product_ready",
                      "met": true
                    },
                    {
                      "name": "sandbox_payment_succeeded",
                      "met": true
                    },
                    {
                      "name": "invoice_presented",
                      "met": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: a KulPay biller, or one not in the sandbox.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "biller EDM is KulPay's own: it goes live by its activation alone",
                  "error": {
                    "code": "invalid_request",
                    "message": "biller EDM is KulPay's own: it goes live by its activation alone"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "createBillProduct",
        "summary": "Configure a product",
        "description": "A product (BIL-1.2): its shape, amount rule, customer reference, supply connectors in priority order, commercial model and fee. The fee is borne by the payer or by the biller, never both (`422 fee_bearer_conflict`): a payer fee cannot come with a supplier discount, the agent model has no discount, and on the principal model with the biller bearing, the discount is the fee. Records `bill_product.created`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^[A-Z0-9_]{2,40}$"
                  },
                  "name": {
                    "type": "string"
                  },
                  "shape": {
                    "type": "string",
                    "enum": [
                      "metered_prepaid",
                      "account_topup",
                      "airtime",
                      "postpaid",
                      "voucher"
                    ]
                  },
                  "currency": {
                    "type": "string"
                  },
                  "amount_rule": {
                    "type": "string",
                    "enum": [
                      "range",
                      "fixed",
                      "denominations",
                      "presented"
                    ]
                  },
                  "min_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Range minimum."
                  },
                  "max_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Range maximum."
                  },
                  "fixed_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Fixed amount."
                  },
                  "denominations": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "format": "int64",
                      "description": "A denomination."
                    }
                  },
                  "reference_label": {
                    "type": "string"
                  },
                  "reference_pattern": {
                    "type": "string",
                    "description": "A regular expression the reference must match."
                  },
                  "reference_example": {
                    "type": "string"
                  },
                  "lookup_required": {
                    "type": "boolean"
                  },
                  "connectors": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Supply connector names, in priority order."
                  },
                  "supplier_product": {
                    "type": "string",
                    "description": "The product's code at the supplier."
                  },
                  "model": {
                    "type": "string",
                    "enum": [
                      "principal",
                      "agent"
                    ]
                  },
                  "fee_bearer": {
                    "type": "string",
                    "enum": [
                      "payer",
                      "biller"
                    ]
                  },
                  "fee_fixed_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Fixed fee."
                  },
                  "fee_bps": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Percentage fee in basis points (150 = 1.50%)."
                  },
                  "fee_min_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Minimum fee."
                  },
                  "fee_max_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Maximum fee."
                  },
                  "discount_bps": {
                    "type": "integer",
                    "format": "int64",
                    "description": "The supplier's discount to KulPay in basis points (principal model, biller bears the fee)."
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "code": "CREDELEC",
                "name": "Credelec",
                "shape": "metered_prepaid",
                "currency": "MZN",
                "amount_rule": "range",
                "min_minor": 2000,
                "max_minor": 5000000,
                "reference_label": "Contador",
                "reference_pattern": "^\\d{11}$",
                "lookup_required": true,
                "connectors": [
                  "fake-test"
                ],
                "model": "principal",
                "fee_bearer": "payer",
                "fee_fixed_minor": 500
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The product, version 1.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "allowed_channels": {},
                    "amount_rule": {
                      "type": "string"
                    },
                    "biller_id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "connectors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "denominations": {},
                    "enabled": {
                      "type": "boolean"
                    },
                    "fee_bearer": {
                      "type": "string"
                    },
                    "fee_bps": {
                      "type": "integer"
                    },
                    "fee_fixed_minor": {
                      "type": "integer"
                    },
                    "fixed_minor": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "lookup_required": {
                      "type": "boolean"
                    },
                    "model": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "reference_label": {
                      "type": "string"
                    },
                    "reference_pattern": {
                      "type": "string"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "supplier_product": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "version": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "allowed_channels": null,
                  "amount_rule": "fixed",
                  "biller_id": "3KWPkylzKmRsF6Rq3xef8pjun2x",
                  "code": "PACOTE",
                  "connectors": [
                    "3KWPkweeFBr3p9OxYa5dTFjzMCF"
                  ],
                  "created_at": "2026-10-10T22:14:57.814553026Z",
                  "currency": "MZN",
                  "denominations": null,
                  "enabled": true,
                  "fee_bearer": "payer",
                  "fee_bps": 0,
                  "fee_fixed_minor": 500,
                  "fixed_minor": 75000,
                  "id": "3KWPkv1wLq7YHJt6Jqg48mMzOKS",
                  "lookup_required": false,
                  "model": "agent",
                  "name": "Pacote",
                  "reference_label": "Smartcard",
                  "reference_pattern": "^\\d{10}$",
                  "shape": "account_topup",
                  "supplier_product": "tv",
                  "updated_at": "2026-10-10T22:14:57.814553026Z",
                  "version": 1
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "reference_label is required: what the payer enters (meter number, phone number\u2026)",
                  "error": {
                    "code": "invalid_request",
                    "message": "reference_label is required: what the payer enters (meter number, phone number\u2026)"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`fee_bearer_conflict`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "fee_bearer_conflict",
                  "detail": "the payer bears the fee, so the biller cannot also give a discount: one party pays, never both",
                  "error": {
                    "code": "fee_bearer_conflict",
                    "message": "the payer bears the fee, so the biller cannot also give a discount: one party pays, never both"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products/{product}": {
      "patch": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "updateBillProduct",
        "summary": "Change a product",
        "description": "Fields left out keep their value. Every change is a new version; payments keep the version they were quoted under. Records `bill_product.updated`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code, e.g. `CREDELEC`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^[A-Z0-9_]{2,40}$"
                  },
                  "name": {
                    "type": "string"
                  },
                  "shape": {
                    "type": "string",
                    "enum": [
                      "metered_prepaid",
                      "account_topup",
                      "airtime",
                      "postpaid",
                      "voucher"
                    ]
                  },
                  "currency": {
                    "type": "string"
                  },
                  "amount_rule": {
                    "type": "string",
                    "enum": [
                      "range",
                      "fixed",
                      "denominations",
                      "presented"
                    ]
                  },
                  "min_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Range minimum."
                  },
                  "max_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Range maximum."
                  },
                  "fixed_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Fixed amount."
                  },
                  "denominations": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "format": "int64",
                      "description": "A denomination."
                    }
                  },
                  "reference_label": {
                    "type": "string"
                  },
                  "reference_pattern": {
                    "type": "string",
                    "description": "A regular expression the reference must match."
                  },
                  "reference_example": {
                    "type": "string"
                  },
                  "lookup_required": {
                    "type": "boolean"
                  },
                  "connectors": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Supply connector names, in priority order."
                  },
                  "supplier_product": {
                    "type": "string",
                    "description": "The product's code at the supplier."
                  },
                  "model": {
                    "type": "string",
                    "enum": [
                      "principal",
                      "agent"
                    ]
                  },
                  "fee_bearer": {
                    "type": "string",
                    "enum": [
                      "payer",
                      "biller"
                    ]
                  },
                  "fee_fixed_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Fixed fee."
                  },
                  "fee_bps": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Percentage fee in basis points (150 = 1.50%)."
                  },
                  "fee_min_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Minimum fee."
                  },
                  "fee_max_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Maximum fee."
                  },
                  "discount_bps": {
                    "type": "integer",
                    "format": "int64",
                    "description": "The supplier's discount to KulPay in basis points (principal model, biller bears the fee)."
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "code": "EDM",
                "name": "Electricidade de Mo\u00e7ambique",
                "shape": "metered_prepaid",
                "currency": "MZN",
                "amount_rule": "range",
                "min_minor": 1,
                "max_minor": 1,
                "fixed_minor": 1,
                "denominations": [
                  1
                ],
                "reference_label": "2003014567",
                "reference_pattern": "2003014567",
                "reference_example": "2003014567",
                "lookup_required": true,
                "connectors": [
                  "string"
                ],
                "supplier_product": "string",
                "model": "principal",
                "fee_bearer": "payer",
                "fee_fixed_minor": 1,
                "fee_bps": 1,
                "fee_min_minor": 1,
                "fee_max_minor": 1,
                "discount_bps": 12,
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "allowed_channels": {},
                    "amount_rule": {
                      "type": "string"
                    },
                    "biller_id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "connectors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "denominations": {},
                    "enabled": {
                      "type": "boolean"
                    },
                    "fee_bearer": {
                      "type": "string"
                    },
                    "fee_bps": {
                      "type": "integer"
                    },
                    "fee_fixed_minor": {
                      "type": "integer"
                    },
                    "fee_max_minor": {
                      "type": "integer"
                    },
                    "fee_min_minor": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "limit_channel_daily_minor": {
                      "type": "integer"
                    },
                    "limit_reference_daily_count": {
                      "type": "integer"
                    },
                    "lookup_required": {
                      "type": "boolean"
                    },
                    "max_minor": {
                      "type": "integer"
                    },
                    "min_minor": {
                      "type": "integer"
                    },
                    "model": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "reference_example": {
                      "type": "string"
                    },
                    "reference_label": {
                      "type": "string"
                    },
                    "reference_pattern": {
                      "type": "string"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "supplier_product": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "version": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "allowed_channels": null,
                  "amount_rule": "range",
                  "biller_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "code": "CREDELEC",
                  "connectors": [
                    "3KUXT4pDy6eRf8sUhJkYc2AwMg6"
                  ],
                  "created_at": "2026-03-02T08:30:00Z",
                  "currency": "MZN",
                  "denominations": null,
                  "enabled": true,
                  "fee_bearer": "payer",
                  "fee_bps": 150,
                  "fee_fixed_minor": 0,
                  "fee_max_minor": 10000,
                  "fee_min_minor": 500,
                  "id": "3KUXU9qFz3gSh1tViLmZd5BxNh7",
                  "limit_channel_daily_minor": 50000000,
                  "limit_reference_daily_count": 5,
                  "lookup_required": true,
                  "max_minor": 5000000,
                  "min_minor": 5000,
                  "model": "principal",
                  "name": "Credelec pr\u00e9-pago",
                  "reference_example": "07012345678",
                  "reference_label": "N\u00famero do contador",
                  "reference_pattern": "^[0-9]{11}$",
                  "shape": "metered_prepaid",
                  "supplier_product": "CREDELEC",
                  "updated_at": "2026-10-11T09:12:44Z",
                  "version": 4
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "fees are non-negative, percentages at most 100%, and fee_min_minor <= fee_max_minor",
                  "error": {
                    "code": "invalid_request",
                    "message": "fees are non-negative, percentages at most 100%, and fee_min_minor <= fee_max_minor"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`fee_bearer_conflict`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "fee_bearer_conflict",
                  "detail": "the payer bears the fee, so the biller cannot also give a discount: one party pays, never both",
                  "error": {
                    "code": "fee_bearer_conflict",
                    "message": "the payer bears the fee, so the biller cannot also give a discount: one party pays, never both"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products/{product}/channels": {
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "setBillProductChannels",
        "summary": "Restrict a product to channels",
        "description": "The channel accounts that may sell the product; an empty list opens it to every channel (BIL-1.3). Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code, e.g. `CREDELEC`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channels": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "channels": [
                  "string"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "allowed_channels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "amount_rule": {
                      "type": "string"
                    },
                    "biller_id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "connectors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "denominations": {},
                    "enabled": {
                      "type": "boolean"
                    },
                    "fee_bearer": {
                      "type": "string"
                    },
                    "fee_bps": {
                      "type": "integer"
                    },
                    "fee_fixed_minor": {
                      "type": "integer"
                    },
                    "fee_max_minor": {
                      "type": "integer"
                    },
                    "fee_min_minor": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "lookup_required": {
                      "type": "boolean"
                    },
                    "max_minor": {
                      "type": "integer"
                    },
                    "min_minor": {
                      "type": "integer"
                    },
                    "model": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "reference_example": {
                      "type": "string"
                    },
                    "reference_label": {
                      "type": "string"
                    },
                    "reference_pattern": {
                      "type": "string"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "supplier_product": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "version": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "allowed_channels": [
                    "3KUXV2rGa7hTj4uWkMnAe8CyPi8",
                    "3KUXW5sHb1iUk7vXmNoBf3DzQj9"
                  ],
                  "amount_rule": "range",
                  "biller_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "code": "CREDELEC",
                  "connectors": [
                    "3KUXT4pDy6eRf8sUhJkYc2AwMg6"
                  ],
                  "created_at": "2026-03-02T08:30:00Z",
                  "currency": "MZN",
                  "denominations": null,
                  "enabled": true,
                  "fee_bearer": "payer",
                  "fee_bps": 150,
                  "fee_fixed_minor": 0,
                  "fee_max_minor": 10000,
                  "fee_min_minor": 500,
                  "id": "3KUXU9qFz3gSh1tViLmZd5BxNh7",
                  "lookup_required": true,
                  "max_minor": 5000000,
                  "min_minor": 5000,
                  "model": "principal",
                  "name": "Credelec pr\u00e9-pago",
                  "reference_example": "07012345678",
                  "reference_label": "N\u00famero do contador",
                  "reference_pattern": "^[0-9]{11}$",
                  "shape": "metered_prepaid",
                  "supplier_product": "CREDELEC",
                  "updated_at": "2026-10-11T09:20:03Z",
                  "version": 5
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no channel account 3KUXX8tJc4jVm1wYnPpCg6EaRk0",
                  "error": {
                    "code": "invalid_request",
                    "message": "no channel account 3KUXX8tJc4jVm1wYnPpCg6EaRk0"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products/{product}/offers": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "saveBillOffer",
        "summary": "Create or change an offer",
        "description": "A bundle or bouquet of the product, by code. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code, e.g. `CREDELEC`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "name",
                  "price_minor"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "price_minor": {
                    "type": "integer",
                    "format": "int64",
                    "description": "Price."
                  },
                  "validity": {
                    "type": "string",
                    "example": "30d"
                  },
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "code": "EDM",
                "name": "Electricidade de Mo\u00e7ambique",
                "price_minor": 1,
                "validity": "30d",
                "active": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The offer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "price_minor": {
                      "type": "integer"
                    },
                    "validity": {
                      "type": "string"
                    },
                    "active": {
                      "type": "boolean"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXY1uKd7kWn4xZoQqDh9FbSl1",
                  "code": "TVBASIC",
                  "name": "Pacote B\u00e1sico",
                  "price_minor": 75000,
                  "validity": "30d",
                  "active": true,
                  "updated_at": "2026-10-11T09:25:18Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "an offer needs a code (capital letters, digits, _), a name and a positive price_minor",
                  "error": {
                    "code": "invalid_request",
                    "message": "an offer needs a code (capital letters, digits, _), a name and a positive price_minor"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products/{product}/offers/refresh": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "refreshBillOffers",
        "summary": "Refresh offers from the supplier",
        "description": "Replaces the product's offers with those its first supply connector lists, when the driver lists offers. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code, e.g. `EDM`."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code, e.g. `CREDELEC`."
          }
        ],
        "responses": {
          "200": {
            "description": "`{refreshed}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "refreshed": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "refreshed": 2
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the edm driver does not list offers",
                  "error": {
                    "code": "invalid_request",
                    "message": "the edm driver does not list offers"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-drivers": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listSupplyDrivers",
        "summary": "Supply drivers",
        "description": "The registered supply drivers with the shapes they serve and their configuration fields (secrets marked). `fake` simulates every shape in test mode; magic references ending 0000 (not found), 9991 (rejected), 9992 (ambiguous, then succeeded), 9993 (float exhausted), 9994 (ambiguous, never known), 9995 (slow), 9996 (unavailable). Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "`{data}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "shapes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "name": "fake",
                      "description": "Simulated supplier for test mode and the local stack; magic references choose the outcome.",
                      "shapes": [
                        "metered_prepaid",
                        "account_topup"
                      ],
                      "fields": [
                        "float_minor",
                        "currency"
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-connectors": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listSupplyConnectors",
        "summary": "Supply connectors",
        "description": "KulPay's own integrations with suppliers, with health, float and configuration (secrets masked). Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "driver": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "enabled": {
                            "type": "boolean"
                          },
                          "health": {
                            "type": "string"
                          },
                          "health_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "float_minor": {
                            "type": "integer"
                          },
                          "float_currency": {
                            "type": "string"
                          },
                          "float_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "config": {
                            "type": "object",
                            "properties": {
                              "float_minor": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                      "name": "fake-test",
                      "driver": "fake",
                      "mode": "test",
                      "enabled": true,
                      "health": "healthy",
                      "health_at": "2026-10-10T22:14:57.762013874Z",
                      "float_minor": 5000000,
                      "float_currency": "MZN",
                      "float_at": "2026-10-10T22:14:57.742829025Z",
                      "created_at": "2026-10-10T22:14:57.717491719Z",
                      "updated_at": "2026-10-10T22:14:57.717491719Z",
                      "config": {
                        "float_minor": 5000000
                      }
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "createSupplyConnector",
        "summary": "Add a supply connector",
        "description": "The configuration is envelope-encrypted; only its keys are recorded in `bill_connector.configured`. The `fake` driver is refused in live mode. Requires `connectors:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_-]{1,59}$"
                  },
                  "driver": {
                    "type": "string",
                    "example": "fake"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "name": "fake-test",
                "driver": "fake",
                "mode": "test",
                "config": {
                  "float_minor": 5000000
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The connector.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "health": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "config": {
                      "type": "object",
                      "properties": {
                        "float_minor": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                  "name": "fake-test",
                  "driver": "fake",
                  "mode": "test",
                  "enabled": true,
                  "health": "healthy",
                  "created_at": "2026-10-10T22:14:57.717491719Z",
                  "updated_at": "2026-10-10T22:14:57.717491719Z",
                  "config": {
                    "float_minor": 5000000
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "mode is live or test",
                  "error": {
                    "code": "invalid_request",
                    "message": "mode is live or test"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-connectors/{name}": {
      "patch": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "updateSupplyConnector",
        "summary": "Change a supply connector",
        "description": "The configuration is merged into the stored one; a secret left out or sent empty keeps its value. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The supply connector name or ID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_-]{1,59}$"
                  },
                  "driver": {
                    "type": "string",
                    "example": "fake"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "low_float_minor": 100000
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The connector.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "health": {
                      "type": "string"
                    },
                    "health_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "float_minor": {
                      "type": "integer"
                    },
                    "float_currency": {
                      "type": "string"
                    },
                    "float_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "low_float_minor": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "config": {
                      "type": "object",
                      "properties": {
                        "float_minor": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                  "name": "fake-test",
                  "driver": "fake",
                  "mode": "test",
                  "enabled": true,
                  "health": "healthy",
                  "health_at": "2026-10-10T22:14:57.762013874Z",
                  "float_minor": 5000000,
                  "float_currency": "MZN",
                  "float_at": "2026-10-10T22:14:57.742829025Z",
                  "low_float_minor": 100000,
                  "created_at": "2026-10-10T22:14:57.717491719Z",
                  "updated_at": "2026-10-10T22:14:57.992068663Z",
                  "config": {
                    "float_minor": 5000000
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the fake driver is for test mode only",
                  "error": {
                    "code": "invalid_request",
                    "message": "the fake driver is for test mode only"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-connectors/{name}/probe": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "probeSupplyConnector",
        "summary": "Probe a supply connector",
        "description": "Asks the supplier for its health and our float when the driver can tell (EDM INT03), and records them. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The supply connector name or ID."
          }
        ],
        "responses": {
          "200": {
            "description": "The connector.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "health": {
                      "type": "string"
                    },
                    "health_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "float_minor": {
                      "type": "integer"
                    },
                    "float_currency": {
                      "type": "string"
                    },
                    "float_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "config": {
                      "type": "object",
                      "properties": {
                        "float_minor": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                  "name": "fake-test",
                  "driver": "fake",
                  "mode": "test",
                  "enabled": true,
                  "health": "healthy",
                  "health_at": "2026-10-10T22:14:57.762013874Z",
                  "float_minor": 5000000,
                  "float_currency": "MZN",
                  "float_at": "2026-10-10T22:14:57.742829025Z",
                  "created_at": "2026-10-10T22:14:57.717491719Z",
                  "updated_at": "2026-10-10T22:14:57.717491719Z",
                  "config": {
                    "float_minor": 5000000
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller, product or supply connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments/lookups": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "lookupBill",
        "summary": "Look up a customer reference",
        "description": "Asks the product's supplier who a meter, account or number belongs to (BIL-2.2) before any money is taken. A found reference returns a `lookup_id` valid for 15 minutes, the customer's name masked, the amount due for postpaid references and the limits a payment must respect; a repeat within that time is answered from the lookup. `status: not_found` keeps nothing. `400 invalid_request` when the reference does not match the product's pattern; `503 biller_unavailable` when no supplier answers. Requires `bills:read`. `customer_name` is masked unless your contract allows full names and KulPay switched them on for you (F3-17); each such lookup is audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "biller",
                  "product",
                  "customer_reference"
                ],
                "properties": {
                  "biller": {
                    "type": "string",
                    "example": "EDM"
                  },
                  "product": {
                    "type": "string",
                    "example": "CREDELEC"
                  },
                  "customer_reference": {
                    "type": "string",
                    "example": "01234567890"
                  }
                }
              },
              "example": {
                "biller": "EDM",
                "product": "CREDELEC",
                "customer_reference": "01234567890"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lookup.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lookup_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "found",
                        "not_found"
                      ]
                    },
                    "biller": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string"
                    },
                    "account": {
                      "type": "string"
                    },
                    "tariff": {
                      "type": "string"
                    },
                    "address": {
                      "type": "string",
                      "description": "The locality of the meter's address."
                    },
                    "debt": {
                      "type": "string",
                      "example": "250.00",
                      "description": "A prepaid meter's debt; debt_percent of each purchase pays it."
                    },
                    "debt_percent": {
                      "type": "string",
                      "example": "20.00"
                    },
                    "amount_due": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "due_date": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "constraints": {
                      "type": "object",
                      "properties": {
                        "min": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "max": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "exact": {
                          "type": "string",
                          "example": "250.00"
                        }
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cached": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "lookup_id": "3KWPl4LcDleeGYpoU9aYN7alhT5",
                  "status": "found",
                  "biller": "EDM",
                  "product": "CREDELEC",
                  "customer_reference": "01234567890",
                  "customer_name": "C****** T**** 7***",
                  "account": "AC01234567890",
                  "tariff": "Prepaid Domestic 1PH",
                  "currency": "MZN",
                  "constraints": {
                    "min": "10.00",
                    "max": "50000.00"
                  },
                  "expires_at": "2026-10-10T22:29:58.620381112Z",
                  "cached": false
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "customer_reference is not a valid contador",
                  "error": {
                    "code": "invalid_request",
                    "message": "customer_reference is not a valid contador"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or product open to you.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`reference_rejected`, or `biller_unavailable`: the biller is not accepting payments (suspended or not yet live); a retry cannot succeed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "reference_rejected",
                  "detail": "Contador 100234567 n\u00e3o existe",
                  "error": {
                    "code": "reference_rejected",
                    "message": "Contador 100234567 n\u00e3o existe"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`biller_unavailable`: the supplier is down or not answering. It passes: retry after `Retry-After` seconds.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "biller_unavailable",
                  "detail": "the supplier is not answering; try again shortly",
                  "error": {
                    "code": "biller_unavailable",
                    "message": "the supplier is not answering; try again shortly"
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments/quotes": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "quoteBill",
        "summary": "Quote a bill payment",
        "description": "Prices a purchase at the product's supplier (BIL-2.3): name the customer with `lookup_id` or `customer_reference` (looked up first when the product requires it) and the value with `amount` or `offer`. The quote has the amount, the payer's fee (when the payer bears it), the total to collect, the supplier's breakdown (units, debt, lines) and `quote_expires_at` (at most 10 minutes). It is pinned to the supplier and the product version that produced it. `422 amount_out_of_range` gives the allowed range. Requires `bills:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "biller": {
                    "type": "string"
                  },
                  "product": {
                    "type": "string"
                  },
                  "customer_reference": {
                    "type": "string"
                  },
                  "lookup_id": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "string",
                    "example": "250.00"
                  },
                  "offer": {
                    "type": "string"
                  },
                  "invoice_id": {
                    "type": "string",
                    "description": "A presented invoice (from GET /v1/payers/{id}/invoices or POST /v1/bill-invoices/lookups): biller and product may be left out; the amount defaults to the open amount."
                  }
                },
                "description": " Quote by `biller` + `product` (+ the reference or a lookup), or by `invoice_id`."
              },
              "example": {
                "biller": "EDM",
                "product": "CREDELEC",
                "lookup_id": "3KWPl4LcDleeGYpoU9aYN7alhT5",
                "amount": "250"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The quote, a bill payment in `quoted`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "bill_payment"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "quoted",
                        "expired",
                        "accepted",
                        "reserved",
                        "sending",
                        "pending",
                        "succeeded",
                        "failed",
                        "review",
                        "reversed"
                      ]
                    },
                    "mode": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "product_version": {
                      "type": "integer"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string",
                      "description": "Masked."
                    },
                    "offer": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "payer_fee": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "total": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee_bearer": {
                      "type": "string",
                      "enum": [
                        "payer",
                        "biller"
                      ]
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "units": {
                          "type": "string",
                          "example": "25.00"
                        },
                        "unit_type": {
                          "type": "string",
                          "example": "kWh"
                        },
                        "debt": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "energy": {
                          "type": "string",
                          "example": "80.00",
                          "description": "What the amount buys once the debt took its part."
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "concept": {
                                "type": "string"
                              },
                              "units": {
                                "type": "string"
                              },
                              "price": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string",
                                "example": "250.00"
                              }
                            }
                          }
                        }
                      }
                    },
                    "quote_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance"
                      ]
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "The id sent to the supplier, persisted before the call."
                    },
                    "supplier_reference": {
                      "type": "string"
                    },
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "token",
                              "pin",
                              "receipt"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "serial": {
                            "type": "string"
                          },
                          "units": {
                            "type": "string"
                          },
                          "unit_type": {
                            "type": "string"
                          },
                          "meta": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                  "object": "bill_payment",
                  "status": "quoted",
                  "mode": "test",
                  "biller": "EDM",
                  "biller_name": "EDM",
                  "product": "CREDELEC",
                  "product_version": 1,
                  "shape": "metered_prepaid",
                  "customer_reference": "01234567890",
                  "customer_name": "C****** T**** 7***",
                  "amount": "250.00",
                  "payer_fee": "5.00",
                  "total": "255.00",
                  "currency": "MZN",
                  "fee_bearer": "payer",
                  "breakdown": {
                    "units": "25.00",
                    "unit_type": "kWh",
                    "lines": [
                      {
                        "concept": "Unit charge per kWh",
                        "units": "25.00",
                        "price": "10.00",
                        "amount": "250.00"
                      }
                    ]
                  },
                  "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                  "created_at": "2026-10-10T22:14:58.651626029Z",
                  "participants": [
                    {
                      "role": "initiating_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B"
                    },
                    {
                      "role": "from_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B",
                      "settlement": "prefunded_balance"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "amount must be a positive decimal",
                  "error": {
                    "code": "invalid_request",
                    "message": "amount must be a positive decimal"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or product open to you.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`amount_out_of_range`, `reference_not_found`, `reference_rejected`; or `biller_unavailable`: the biller is not accepting payments; a retry cannot succeed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "amount_out_of_range",
                  "detail": "the amount must be between 20.00 MZN and 50000.00 MZN",
                  "error": {
                    "code": "amount_out_of_range",
                    "message": "the amount must be between 20.00 MZN and 50000.00 MZN"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`biller_unavailable`: the supplier is down or not answering. It passes: retry after `Retry-After` seconds.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "biller_unavailable",
                  "detail": "the supplier is not answering; try again shortly",
                  "error": {
                    "code": "biller_unavailable",
                    "message": "the supplier is not answering; try again shortly"
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "payBill",
        "summary": "Pay a quote",
        "description": "Pays a quote from your prefunded balance (BIL-2.4): the total is reserved, a request id is persisted and sent to the supplier. `201` with the payment `succeeded` (tokens, PINs or receipt in `artifacts`) or `failed` (nothing charged, the reserve released); `202` when the supplier has not answered within 25 seconds or gave no definitive answer: the payment is `sending` or `pending`, and KulPay queries the supplier with the same request id until it knows. Follow it with the `bill_payment.succeeded`, `bill_payment.failed` and `bill_payment.pending` webhooks or `GET /v1/bill-payments/{id}`; never pay again for the same purchase. Channels without a balance pay with `funding: \"collect\"` and `collect: {method, phone}`: the total is collected from the payer through the payment rails first (`collection_id`, `bill_payment.collecting`), the bill is sent once it succeeds, and a bill that then fails is refunded to the payer (`bill_payment.refunded`). A quote is paid once (`409 quote_used`); an expired one is refused (`422 quote_expired`); `422 insufficient_funds` leaves the quote payable. Requires `bills:write` and an `Idempotency-Key`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quote_id"
                ],
                "properties": {
                  "quote_id": {
                    "type": "string"
                  },
                  "funding": {
                    "type": "string",
                    "enum": [
                      "balance"
                    ],
                    "default": "balance"
                  },
                  "partner_reference": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "payer": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string",
                        "format": "email"
                      }
                    }
                  },
                  "collect": {
                    "type": "object",
                    "description": "With funding collect: how KulPay collects the total from the payer.",
                    "properties": {
                      "method": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      },
                      "network": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "quote_id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                "partner_reference": "POS-9",
                "payer": {
                  "phone": "258841234567"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Succeeded or failed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "bill_payment"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "quoted",
                        "expired",
                        "accepted",
                        "reserved",
                        "sending",
                        "pending",
                        "succeeded",
                        "failed",
                        "review",
                        "reversed"
                      ]
                    },
                    "mode": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "product_version": {
                      "type": "integer"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string",
                      "description": "Masked."
                    },
                    "offer": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "payer_fee": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "total": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee_bearer": {
                      "type": "string",
                      "enum": [
                        "payer",
                        "biller"
                      ]
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "units": {
                          "type": "string",
                          "example": "25.00"
                        },
                        "unit_type": {
                          "type": "string",
                          "example": "kWh"
                        },
                        "debt": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "energy": {
                          "type": "string",
                          "example": "80.00",
                          "description": "What the amount buys once the debt took its part."
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "concept": {
                                "type": "string"
                              },
                              "units": {
                                "type": "string"
                              },
                              "price": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string",
                                "example": "250.00"
                              }
                            }
                          }
                        }
                      }
                    },
                    "quote_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance"
                      ]
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "The id sent to the supplier, persisted before the call."
                    },
                    "supplier_reference": {
                      "type": "string"
                    },
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "token",
                              "pin",
                              "receipt"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "serial": {
                            "type": "string"
                          },
                          "units": {
                            "type": "string"
                          },
                          "unit_type": {
                            "type": "string"
                          },
                          "meta": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                  "object": "bill_payment",
                  "status": "succeeded",
                  "mode": "test",
                  "biller": "EDM",
                  "biller_name": "EDM",
                  "product": "CREDELEC",
                  "product_version": 1,
                  "shape": "metered_prepaid",
                  "customer_reference": "01234567890",
                  "customer_name": "C****** T**** 7***",
                  "amount": "250.00",
                  "payer_fee": "5.00",
                  "total": "255.00",
                  "currency": "MZN",
                  "fee_bearer": "payer",
                  "breakdown": {
                    "units": "25.00",
                    "unit_type": "kWh",
                    "lines": [
                      {
                        "concept": "Unit charge per kWh",
                        "units": "25.00",
                        "price": "10.00",
                        "amount": "250.00"
                      }
                    ]
                  },
                  "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                  "funding": "balance",
                  "partner_reference": "POS-9",
                  "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                  "supplier_reference": "FK2026101045631213",
                  "artifacts": [
                    {
                      "type": "receipt",
                      "value": "FK2026101045631213"
                    },
                    {
                      "type": "token",
                      "value": "69556183477675547957",
                      "units": "25.00",
                      "unit_type": "kWh",
                      "meta": {
                        "krn": "1",
                        "sgc": "600123",
                        "ti": "1"
                      }
                    }
                  ],
                  "receipt_url": "/bills/receipt/3KWPl4pk0XxRI4TEVI6avRgrj1y?t=plain%3AYmlsbC1yZWNlaXB0fDNLV1BsNHBrMFh4Ukk0VEVWSTZhdlJncmoxeQ",
                  "created_at": "2026-10-10T22:14:58.651626029Z",
                  "completed_at": "2026-10-10T22:14:58.744497415Z",
                  "participants": [
                    {
                      "role": "initiating_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B"
                    },
                    {
                      "role": "from_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B",
                      "settlement": "prefunded_balance"
                    }
                  ]
                }
              }
            }
          },
          "202": {
            "description": "Sending or pending.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "bill_payment"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "quoted",
                        "expired",
                        "accepted",
                        "reserved",
                        "sending",
                        "pending",
                        "succeeded",
                        "failed",
                        "review",
                        "reversed"
                      ]
                    },
                    "mode": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "product_version": {
                      "type": "integer"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string",
                      "description": "Masked."
                    },
                    "offer": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "payer_fee": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "total": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee_bearer": {
                      "type": "string",
                      "enum": [
                        "payer",
                        "biller"
                      ]
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "units": {
                          "type": "string",
                          "example": "25.00"
                        },
                        "unit_type": {
                          "type": "string",
                          "example": "kWh"
                        },
                        "debt": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "energy": {
                          "type": "string",
                          "example": "80.00",
                          "description": "What the amount buys once the debt took its part."
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "concept": {
                                "type": "string"
                              },
                              "units": {
                                "type": "string"
                              },
                              "price": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string",
                                "example": "250.00"
                              }
                            }
                          }
                        }
                      }
                    },
                    "quote_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance"
                      ]
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "The id sent to the supplier, persisted before the call."
                    },
                    "supplier_reference": {
                      "type": "string"
                    },
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "token",
                              "pin",
                              "receipt"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "serial": {
                            "type": "string"
                          },
                          "units": {
                            "type": "string"
                          },
                          "unit_type": {
                            "type": "string"
                          },
                          "meta": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "object": "bill_payment",
                  "status": "quoted",
                  "mode": "test",
                  "biller": "EDM",
                  "biller_name": "Electricidade de Mo\u00e7ambique",
                  "product": "CREDELEC",
                  "product_version": 1,
                  "shape": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "customer_reference": "2003014567",
                  "customer_name": "Electricidade de Mo\u00e7ambique",
                  "offer": "string",
                  "amount": "250.00",
                  "payer_fee": "250.00",
                  "total": "250.00",
                  "currency": "MZN",
                  "fee_bearer": "payer",
                  "breakdown": {
                    "units": "25.00",
                    "unit_type": "kWh",
                    "debt": "250.00",
                    "lines": [
                      {
                        "concept": "string",
                        "units": "string",
                        "price": "string",
                        "amount": "250.00"
                      }
                    ]
                  },
                  "quote_expires_at": "2026-10-10T09:30:00Z",
                  "funding": "balance",
                  "partner_reference": "2003014567",
                  "request_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "supplier_reference": "2003014567",
                  "artifacts": [
                    {
                      "type": "token",
                      "value": "1250.00",
                      "serial": "string",
                      "units": "string",
                      "unit_type": "string",
                      "meta": {
                        "key": "1250.00"
                      }
                    }
                  ],
                  "failure_code": "EDM",
                  "failure_message": "Pagamento de energia, outubro",
                  "created_at": "2026-10-10T09:30:00Z",
                  "completed_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such quote.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`quote_used`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "quote_used",
                  "detail": "this quote was already paid (bill payment 3KWPl4pk0XxRI4TEVI6avRgrj1y is succeeded)",
                  "error": {
                    "code": "quote_used",
                    "message": "this quote was already paid (bill payment 3KWPl4pk0XxRI4TEVI6avRgrj1y is succeeded)"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`quote_expired`, `insufficient_funds`, or `biller_unavailable`: the biller is not accepting payments. `pilot_limit` while the account is a PSP in pilot: the live transaction breaks the pilot setting named in `rule` (`per_transaction`, `daily`, `monthly`, `currency`, `settings_missing`), checked for either funding before anything is reserved or collected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "insufficient_balance",
                  "detail": "the balance (10.00 MZN) does not cover 255.00 MZN",
                  "error": {
                    "code": "insufficient_balance",
                    "message": "the balance (10.00 MZN) does not cover 255.00 MZN"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`biller_unavailable`: the supplier is down or not answering. It passes: retry after `Retry-After` seconds.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "biller_unavailable",
                  "detail": "the supplier is not answering; try again shortly",
                  "error": {
                    "code": "biller_unavailable",
                    "message": "the supplier is not answering; try again shortly"
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listBillPayments",
        "summary": "Your bill payments",
        "description": "Newest first, filtered by `status`, `biller`, `customer_reference`, up to `limit` (100, at most 500). Requires `bills:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "biller",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_reference",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "object": {
                            "type": "string",
                            "enum": [
                              "bill_payment"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "quoted",
                              "expired",
                              "accepted",
                              "reserved",
                              "sending",
                              "pending",
                              "succeeded",
                              "failed",
                              "review",
                              "reversed"
                            ]
                          },
                          "mode": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "product_version": {
                            "type": "integer"
                          },
                          "shape": {
                            "type": "string"
                          },
                          "customer_reference": {
                            "type": "string"
                          },
                          "customer_name": {
                            "type": "string",
                            "description": "Masked."
                          },
                          "offer": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string",
                            "example": "250.00"
                          },
                          "payer_fee": {
                            "type": "string",
                            "example": "250.00"
                          },
                          "total": {
                            "type": "string",
                            "example": "250.00"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "fee_bearer": {
                            "type": "string",
                            "enum": [
                              "payer",
                              "biller"
                            ]
                          },
                          "breakdown": {
                            "type": "object",
                            "properties": {
                              "units": {
                                "type": "string",
                                "example": "25.00"
                              },
                              "unit_type": {
                                "type": "string",
                                "example": "kWh"
                              },
                              "debt": {
                                "type": "string",
                                "example": "250.00"
                              },
                              "lines": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "concept": {
                                      "type": "string"
                                    },
                                    "units": {
                                      "type": "string"
                                    },
                                    "price": {
                                      "type": "string"
                                    },
                                    "amount": {
                                      "type": "string",
                                      "example": "250.00"
                                    }
                                  }
                                }
                              }
                            }
                          },
                          "quote_expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "funding": {
                            "type": "string",
                            "enum": [
                              "balance"
                            ]
                          },
                          "partner_reference": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string",
                            "description": "The id sent to the supplier, persisted before the call."
                          },
                          "supplier_reference": {
                            "type": "string"
                          },
                          "artifacts": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string",
                                  "enum": [
                                    "token",
                                    "pin",
                                    "receipt"
                                  ]
                                },
                                "value": {
                                  "type": "string"
                                },
                                "serial": {
                                  "type": "string"
                                },
                                "units": {
                                  "type": "string"
                                },
                                "unit_type": {
                                  "type": "string"
                                },
                                "meta": {
                                  "type": "object",
                                  "additionalProperties": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          },
                          "failure_code": {
                            "type": "string"
                          },
                          "failure_message": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "completed_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                      "object": "bill_payment",
                      "status": "succeeded",
                      "mode": "test",
                      "biller": "EDM",
                      "biller_name": "EDM",
                      "product": "CREDELEC",
                      "product_version": 1,
                      "shape": "metered_prepaid",
                      "customer_reference": "01234567890",
                      "customer_name": "C****** T**** 7***",
                      "amount": "250.00",
                      "payer_fee": "5.00",
                      "total": "255.00",
                      "currency": "MZN",
                      "fee_bearer": "payer",
                      "breakdown": {
                        "units": "25.00",
                        "unit_type": "kWh",
                        "lines": [
                          {
                            "concept": "Unit charge per kWh",
                            "units": "25.00",
                            "price": "10.00",
                            "amount": "250.00"
                          }
                        ]
                      },
                      "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                      "funding": "balance",
                      "partner_reference": "POS-9",
                      "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                      "supplier_reference": "FK2026101045631213",
                      "artifacts": [
                        {
                          "type": "receipt",
                          "value": "FK2026101045631213"
                        },
                        {
                          "type": "token",
                          "value": "69556183477675547957",
                          "units": "25.00",
                          "unit_type": "kWh",
                          "meta": {
                            "krn": "1",
                            "sgc": "600123",
                            "ti": "1"
                          }
                        }
                      ],
                      "receipt_url": "/bills/receipt/3KWPl4pk0XxRI4TEVI6avRgrj1y?t=plain%3AYmlsbC1yZWNlaXB0fDNLV1BsNHBrMFh4Ukk0VEVWSTZhdlJncmoxeQ",
                      "created_at": "2026-10-10T22:14:58.651626029Z",
                      "completed_at": "2026-10-10T22:14:58.744497415Z",
                      "participants": [
                        {
                          "role": "initiating_psp",
                          "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                          "name": "Tenant B"
                        },
                        {
                          "role": "from_psp",
                          "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                          "name": "Tenant B",
                          "settlement": "prefunded_balance"
                        }
                      ]
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments/{id}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillPayment",
        "summary": "A bill payment",
        "description": "With its artifacts once succeeded. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bill payment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string",
                      "enum": [
                        "bill_payment"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "quoted",
                        "expired",
                        "accepted",
                        "reserved",
                        "sending",
                        "pending",
                        "succeeded",
                        "failed",
                        "review",
                        "reversed"
                      ]
                    },
                    "mode": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "product_version": {
                      "type": "integer"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string",
                      "description": "Masked."
                    },
                    "offer": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "payer_fee": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "total": {
                      "type": "string",
                      "example": "250.00"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee_bearer": {
                      "type": "string",
                      "enum": [
                        "payer",
                        "biller"
                      ]
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "units": {
                          "type": "string",
                          "example": "25.00"
                        },
                        "unit_type": {
                          "type": "string",
                          "example": "kWh"
                        },
                        "debt": {
                          "type": "string",
                          "example": "250.00"
                        },
                        "energy": {
                          "type": "string",
                          "example": "80.00",
                          "description": "What the amount buys once the debt took its part."
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "concept": {
                                "type": "string"
                              },
                              "units": {
                                "type": "string"
                              },
                              "price": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string",
                                "example": "250.00"
                              }
                            }
                          }
                        }
                      }
                    },
                    "quote_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance"
                      ]
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string",
                      "description": "The id sent to the supplier, persisted before the call."
                    },
                    "supplier_reference": {
                      "type": "string"
                    },
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "token",
                              "pin",
                              "receipt"
                            ]
                          },
                          "value": {
                            "type": "string"
                          },
                          "serial": {
                            "type": "string"
                          },
                          "units": {
                            "type": "string"
                          },
                          "unit_type": {
                            "type": "string"
                          },
                          "meta": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                  "object": "bill_payment",
                  "status": "succeeded",
                  "mode": "test",
                  "biller": "EDM",
                  "biller_name": "EDM",
                  "product": "CREDELEC",
                  "product_version": 1,
                  "shape": "metered_prepaid",
                  "customer_reference": "01234567890",
                  "customer_name": "C****** T**** 7***",
                  "amount": "250.00",
                  "payer_fee": "5.00",
                  "total": "255.00",
                  "currency": "MZN",
                  "fee_bearer": "payer",
                  "breakdown": {
                    "units": "25.00",
                    "unit_type": "kWh",
                    "lines": [
                      {
                        "concept": "Unit charge per kWh",
                        "units": "25.00",
                        "price": "10.00",
                        "amount": "250.00"
                      }
                    ]
                  },
                  "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                  "funding": "balance",
                  "partner_reference": "POS-9",
                  "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                  "supplier_reference": "FK2026101045631213",
                  "artifacts": [
                    {
                      "type": "receipt",
                      "value": "FK2026101045631213"
                    },
                    {
                      "type": "token",
                      "value": "69556183477675547957",
                      "units": "25.00",
                      "unit_type": "kWh",
                      "meta": {
                        "krn": "1",
                        "sgc": "600123",
                        "ti": "1"
                      }
                    }
                  ],
                  "receipt_url": "/bills/receipt/3KWPl4pk0XxRI4TEVI6avRgrj1y?t=plain%3AYmlsbC1yZWNlaXB0fDNLV1BsNHBrMFh4Ukk0VEVWSTZhdlJncmoxeQ",
                  "created_at": "2026-10-10T22:14:58.651626029Z",
                  "completed_at": "2026-10-10T22:14:58.744497415Z",
                  "participants": [
                    {
                      "role": "initiating_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B"
                    },
                    {
                      "role": "from_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B",
                      "settlement": "prefunded_balance"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-payments": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminListBillPayments",
        "summary": "Bill payments across channels",
        "description": "Every channel's bill payments, newest first, filtered by `status` (`review` is the review queue), `biller`, `channel` and `customer_reference`, with the request id, attempts and queries. Tokens and PINs are masked. Requires `admin:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "biller",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_reference",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "object": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "product_version": {
                            "type": "integer"
                          },
                          "shape": {
                            "type": "string"
                          },
                          "customer_reference": {
                            "type": "string"
                          },
                          "customer_name": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "payer_fee": {
                            "type": "string"
                          },
                          "total": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "fee_bearer": {
                            "type": "string"
                          },
                          "breakdown": {
                            "type": "object",
                            "properties": {
                              "units": {
                                "type": "string"
                              },
                              "unit_type": {
                                "type": "string"
                              },
                              "lines": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "concept": {
                                      "type": "string"
                                    },
                                    "units": {
                                      "type": "string"
                                    },
                                    "price": {
                                      "type": "string"
                                    },
                                    "amount": {
                                      "type": "string"
                                    }
                                  }
                                }
                              }
                            }
                          },
                          "quote_expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "funding": {
                            "type": "string"
                          },
                          "partner_reference": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "supplier_reference": {
                            "type": "string"
                          },
                          "artifacts": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string"
                                },
                                "value": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "completed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "participants": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "role": {
                                  "type": "string"
                                },
                                "account_id": {
                                  "type": "string"
                                },
                                "name": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "channel_id": {
                            "type": "string"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "queries": {
                            "type": "integer"
                          },
                          "sent_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                      "object": "bill_payment",
                      "status": "succeeded",
                      "mode": "test",
                      "biller": "EDM",
                      "biller_name": "EDM",
                      "product": "CREDELEC",
                      "product_version": 1,
                      "shape": "metered_prepaid",
                      "customer_reference": "01234567890",
                      "customer_name": "C****** T**** 7***",
                      "amount": "250.00",
                      "payer_fee": "5.00",
                      "total": "255.00",
                      "currency": "MZN",
                      "fee_bearer": "payer",
                      "breakdown": {
                        "units": "25.00",
                        "unit_type": "kWh",
                        "lines": [
                          {
                            "concept": "Unit charge per kWh",
                            "units": "25.00",
                            "price": "10.00",
                            "amount": "250.00"
                          }
                        ]
                      },
                      "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                      "funding": "balance",
                      "partner_reference": "POS-9",
                      "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                      "supplier_reference": "FK2026101045631213",
                      "artifacts": [
                        {
                          "type": "receipt",
                          "value": "FK2026101045631213"
                        },
                        {
                          "type": "token",
                          "value": "****************7957",
                          "units": "25.00",
                          "unit_type": "kWh",
                          "meta": {
                            "krn": "1",
                            "sgc": "600123",
                            "ti": "1"
                          }
                        }
                      ],
                      "created_at": "2026-10-10T22:14:58.651626029Z",
                      "completed_at": "2026-10-10T22:14:58.744497415Z",
                      "participants": [
                        {
                          "role": "initiating_psp",
                          "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                          "name": "Tenant B"
                        },
                        {
                          "role": "from_psp",
                          "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                          "name": "Tenant B",
                          "settlement": "prefunded_balance"
                        }
                      ],
                      "channel_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "attempts": 1,
                      "queries": 0,
                      "sent_at": "2026-10-10T22:14:58.727064442Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-payments/{id}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminGetBillPayment",
        "summary": "A bill payment (operators)",
        "description": "Tokens and PINs masked. Requires `admin:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bill payment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "product_version": {
                      "type": "integer"
                    },
                    "shape": {
                      "type": "string"
                    },
                    "customer_reference": {
                      "type": "string"
                    },
                    "customer_name": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "payer_fee": {
                      "type": "string"
                    },
                    "total": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "fee_bearer": {
                      "type": "string"
                    },
                    "breakdown": {
                      "type": "object",
                      "properties": {
                        "units": {
                          "type": "string"
                        },
                        "unit_type": {
                          "type": "string"
                        },
                        "lines": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "concept": {
                                "type": "string"
                              },
                              "units": {
                                "type": "string"
                              },
                              "price": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    "quote_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "funding": {
                      "type": "string"
                    },
                    "partner_reference": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string"
                    },
                    "supplier_reference": {
                      "type": "string"
                    },
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "participants": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "role": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "channel_id": {
                      "type": "string"
                    },
                    "attempts": {
                      "type": "integer"
                    },
                    "queries": {
                      "type": "integer"
                    },
                    "sent_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPl4pk0XxRI4TEVI6avRgrj1y",
                  "object": "bill_payment",
                  "status": "succeeded",
                  "mode": "test",
                  "biller": "EDM",
                  "biller_name": "EDM",
                  "product": "CREDELEC",
                  "product_version": 1,
                  "shape": "metered_prepaid",
                  "customer_reference": "01234567890",
                  "customer_name": "C****** T**** 7***",
                  "amount": "250.00",
                  "payer_fee": "5.00",
                  "total": "255.00",
                  "currency": "MZN",
                  "fee_bearer": "payer",
                  "breakdown": {
                    "units": "25.00",
                    "unit_type": "kWh",
                    "lines": [
                      {
                        "concept": "Unit charge per kWh",
                        "units": "25.00",
                        "price": "10.00",
                        "amount": "250.00"
                      }
                    ]
                  },
                  "quote_expires_at": "2026-10-10T22:24:58.651608397Z",
                  "funding": "balance",
                  "partner_reference": "POS-9",
                  "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                  "supplier_reference": "FK2026101045631213",
                  "artifacts": [
                    {
                      "type": "receipt",
                      "value": "FK2026101045631213"
                    },
                    {
                      "type": "token",
                      "value": "****************7957",
                      "units": "25.00",
                      "unit_type": "kWh",
                      "meta": {
                        "krn": "1",
                        "sgc": "600123",
                        "ti": "1"
                      }
                    }
                  ],
                  "created_at": "2026-10-10T22:14:58.651626029Z",
                  "completed_at": "2026-10-10T22:14:58.744497415Z",
                  "participants": [
                    {
                      "role": "initiating_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B"
                    },
                    {
                      "role": "from_psp",
                      "account_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                      "name": "Tenant B",
                      "settlement": "prefunded_balance"
                    }
                  ],
                  "channel_id": "3KWPl6gHAomFsLp2Cbl41VxBTab",
                  "attempts": 1,
                  "queries": 0,
                  "sent_at": "2026-10-10T22:14:58.727064442Z"
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-payments/{id}/resolve": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "resolveBillPayment",
        "summary": "Resolve a bill payment in review",
        "description": "A payment goes to review when querying its supplier with the same request id has not settled it within 24 hours (BIL-2.5). An operator records what the supplier did: `succeeded` with the supplier's reference and, if one was issued, the token or PIN (sealed in the approval), or `failed`. A second operator approves; the payment then succeeds (the reserve settled, `bill_payment.succeeded`) or fails (released, `bill_payment.failed`), with `bill_payment.resolved`. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcome",
                  "reason"
                ],
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "succeeded",
                      "failed"
                    ]
                  },
                  "supplier_reference": {
                    "type": "string"
                  },
                  "token": {
                    "type": "string"
                  },
                  "units": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "outcome": "succeeded",
                "supplier_reference": "2003014567",
                "token": "tok_7Hq2\u2026",
                "units": "string",
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KUYO4KZt1AMD7NPEGgTx2VrIB7",
                    "kind": "bill.resolve",
                    "subject_id": "3KUYJ7FUo4vHy1IKzBbOs5QmDw2",
                    "tenant_id": "3KUXV2rGa7hTj4uWkMnAe8CyPi8",
                    "payload": "{\"outcome\":\"failed\"}",
                    "reason": "EDM confirmed by phone that no token was issued for this request",
                    "prepared_by": "8f3c2a7e5b1d4c6a9e0f2b7d4a1c5e83",
                    "status": "pending",
                    "created_at": "2026-10-11T10:02:37Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: not in review, or a delivery without the supplier's reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "only a payment in review is resolved by hand (this one is succeeded)",
                  "error": {
                    "code": "invalid_request",
                    "message": "only a payment in review is resolved by hand (this one is succeeded)"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments/{id}/resend": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "resendBillArtifacts",
        "summary": "Send the payer their token or receipt again",
        "description": "Sends a succeeded payment's token, PIN or receipt to the payer again (BIL-2.6), by `email` (through the notification outbox) or `sms` (`503 sms_unavailable` when the gateway has no SMS channel), to `to` or the contact given when paying. At most three times per payment (`422 resend_limit`). The payment also carries `receipt_url`, a signed hosted receipt that shows the token once the payer confirms the phone number given when paying. Requires `bills:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "via"
                ],
                "properties": {
                  "via": {
                    "type": "string",
                    "enum": [
                      "sms",
                      "email"
                    ]
                  },
                  "to": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "via": "email",
                "to": "ana@example.com"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{via, to}`, the recipient masked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "to": {
                      "type": "string"
                    },
                    "via": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "to": "***********.com",
                  "via": "email"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "via is sms or email",
                  "error": {
                    "code": "invalid_request",
                    "message": "via is sms or email"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`resend_limit`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "resend_limit",
                  "detail": "the artifacts were already sent again 3 times",
                  "error": {
                    "code": "resend_limit",
                    "message": "the artifacts were already sent again 3 times"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`sms_unavailable`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "sms_unavailable",
                  "detail": "this gateway has no SMS channel configured; send by email",
                  "error": {
                    "code": "sms_unavailable",
                    "message": "this gateway has no SMS channel configured; send by email"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-payments/{id}/artifacts": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "revealBillArtifacts",
        "summary": "See tokens and PINs in clear (operators)",
        "description": "Operators otherwise see tokens and PINs masked. A `reason` is required and every use records `bill_payment.artifacts_revealed` with the operator. Requires `bills:artifacts`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "support ticket 1"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{artifacts}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "artifacts": [
                    {
                      "type": "receipt",
                      "value": "FK2026101045631213"
                    },
                    {
                      "type": "token",
                      "value": "69556183477675547957",
                      "units": "25.00",
                      "unit_type": "kWh",
                      "meta": {
                        "krn": "1",
                        "sgc": "600123",
                        "ti": "1"
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "No reason.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a reason is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a reason is required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-connectors/{name}/float-topups": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "requestFloatTopUp",
        "summary": "Record a float top-up",
        "description": "Money treasury sent to a supplier for our float (BIL-3.4): the amount, the transfer's bank reference and a reason. A second person approves it (`bill_float.topped_up`); the next probe expects the supplier's float to have grown by it, and records `bill_float.mismatch` when it has not. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_minor",
                  "bank_reference",
                  "reason"
                ],
                "properties": {
                  "amount_minor": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "bank_reference": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "amount_minor": 500000,
                "bank_reference": "BCI-1",
                "reason": "monthly"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The top-up, `pending` with its `approval_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "connector_id": {
                      "type": "string"
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "bank_reference": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPl6P3YYG5B9DI0Klb9fMlSUu",
                  "connector_id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                  "amount_minor": 500000,
                  "currency": "MZN",
                  "bank_reference": "BCI-1",
                  "status": "pending",
                  "approval_id": "3KWPlAON5BFKuGPEjNuGpBV06KB",
                  "created_by": "key_accounts",
                  "created_at": "2026-10-10T22:14:58.017507082Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a positive amount_minor and the bank_reference of the transfer are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a positive amount_minor and the bank_reference of the transfer are required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/float-topups": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listFloatTopUps",
        "summary": "Float top-ups",
        "description": "Newest first, of one `connector` or all. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "connector",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "connector_id": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "bank_reference": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPl6P3YYG5B9DI0Klb9fMlSUu",
                      "connector_id": "3KWPkweeFBr3p9OxYa5dTFjzMCF",
                      "amount_minor": 500000,
                      "currency": "MZN",
                      "bank_reference": "BCI-1",
                      "status": "pending",
                      "approval_id": "3KWPlAON5BFKuGPEjNuGpBV06KB",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:14:58.017507082Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/bill-balance-alert": {
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putBillBalanceAlert",
        "summary": "A channel's low-balance alert",
        "description": "The balance below which a channel paying bills from its balance is sent `bill_balance.low` (once, until it is back above). `low_balance_minor: 0` turns it off. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currency",
                  "low_balance_minor"
                ],
                "properties": {
                  "currency": {
                    "type": "string"
                  },
                  "low_balance_minor": {
                    "type": "integer",
                    "format": "int64"
                  }
                }
              },
              "example": {
                "currency": "MZN",
                "low_balance_minor": 50000
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The setting.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel_id": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "low_balance_minor": {
                      "type": "integer"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "channel_id": "3KWPkvN7G0ryWRJbNcBl0bbDmWS",
                  "currency": "MZN",
                  "low_balance_minor": 50000,
                  "updated_at": "2026-10-10T22:14:58.039503731Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no such account",
                  "error": {
                    "code": "invalid_request",
                    "message": "no such account"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "presentInvoices",
        "summary": "Present invoices",
        "description": "A biller that presents by push sends its invoices (BIL-5.2): one invoice, or `{invoices: [...]}` up to 1,000, answered per invoice (`created`, `updated`, `rejected` with the reason). The number is unique per biller; presenting it again updates an unpaid invoice. Lines, when given, must add up to the amount. Channels pay an invoice by quoting with `invoice_id` (`POST /v1/bill-payments/quotes`): the amount defaults to what is still open; a partial or larger payment only when the biller accepts it. When a payment succeeds the biller receives `bill_invoice.paid`. `403 not_a_biller` when the key's organisation is not a biller. Requires `invoices:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "number",
                      "account_reference",
                      "amount",
                      "currency",
                      "due_date"
                    ],
                    "properties": {
                      "number": {
                        "type": "string",
                        "maxLength": 64
                      },
                      "account_reference": {
                        "type": "string",
                        "maxLength": 64
                      },
                      "amount": {
                        "type": "string",
                        "example": "1500.00"
                      },
                      "currency": {
                        "type": "string"
                      },
                      "issue_date": {
                        "type": "string",
                        "format": "date"
                      },
                      "due_date": {
                        "type": "string",
                        "format": "date"
                      },
                      "description": {
                        "type": "string"
                      },
                      "pdf_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "lines": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "description": {
                              "type": "string"
                            },
                            "amount": {
                              "type": "string",
                              "example": "1500.00"
                            }
                          }
                        }
                      }
                    }
                  },
                  {
                    "type": "object",
                    "properties": {
                      "invoices": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "required": [
                            "number",
                            "account_reference",
                            "amount",
                            "currency",
                            "due_date"
                          ],
                          "properties": {
                            "number": {
                              "type": "string",
                              "maxLength": 64
                            },
                            "account_reference": {
                              "type": "string",
                              "maxLength": 64
                            },
                            "amount": {
                              "type": "string",
                              "example": "1500.00"
                            },
                            "currency": {
                              "type": "string"
                            },
                            "issue_date": {
                              "type": "string",
                              "format": "date"
                            },
                            "due_date": {
                              "type": "string",
                              "format": "date"
                            },
                            "description": {
                              "type": "string"
                            },
                            "pdf_url": {
                              "type": "string",
                              "format": "uri"
                            },
                            "lines": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "description": {
                                    "type": "string"
                                  },
                                  "amount": {
                                    "type": "string",
                                    "example": "1500.00"
                                  }
                                }
                              }
                            }
                          }
                        },
                        "maxItems": 1000
                      }
                    }
                  }
                ]
              },
              "example": {
                "number": "F-9",
                "account_reference": "CL-9",
                "amount": "300",
                "currency": "MZN",
                "due_date": "2026-12-31"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{results, created, updated, rejected}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "integer"
                    },
                    "rejected": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "number": {
                            "type": "string"
                          },
                          "result": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "updated": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "created": 2,
                  "rejected": 1,
                  "results": [
                    {
                      "number": "F-1",
                      "result": "created",
                      "invoice_id": "3KWPlCZORCtehfKOAIkXyowwAqr"
                    },
                    {
                      "number": "F-2",
                      "result": "created",
                      "invoice_id": "3KWPlGyWnT4jx8g7CGpEoWP0GHe"
                    }
                  ],
                  "updated": 0
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: not set up to present by push, or an empty batch.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "send 1 to 1000 invoices",
                  "error": {
                    "code": "invalid_request",
                    "message": "send 1 to 1000 invoices"
                  }
                }
              }
            }
          },
          "403": {
            "description": "`not_a_biller`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: invoices:write",
                  "missing_scope": "invoices:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: invoices:write",
                    "missing_scope": "invoices:write"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listInvoices",
        "summary": "Your invoices",
        "description": "Filtered by `status` and `account_reference`, by due date. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "partially_paid",
                "paid",
                "cancelled",
                "expired"
              ]
            }
          },
          {
            "name": "account_reference",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`: each with amount, paid and open.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "number": {
                            "type": "string"
                          },
                          "account_reference": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "paid": {
                            "type": "string"
                          },
                          "open": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "issue_date": {
                            "type": "string"
                          },
                          "due_date": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlCZORCtehfKOAIkXyowwAqr",
                      "biller": "AGUAS",
                      "biller_name": "\u00c1guas",
                      "number": "F-1",
                      "account_reference": "CL-1",
                      "amount": "1500.00",
                      "paid": "1500.00",
                      "open": "0.00",
                      "currency": "MZN",
                      "issue_date": "2026-10-10",
                      "due_date": "2026-10-31",
                      "status": "paid",
                      "expires_at": "2026-11-30T00:00:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices/{number}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getInvoice",
        "summary": "One of your invoices",
        "description": "Requires `invoices:read`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "number": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "paid": {
                      "type": "string"
                    },
                    "open": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "issue_date": {
                      "type": "string"
                    },
                    "due_date": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "engagement": {
                      "type": "object",
                      "properties": {
                        "opened": {
                          "type": "integer"
                        },
                        "pinned": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPlCZORCtehfKOAIkXyowwAqr",
                  "biller": "AGUAS",
                  "biller_name": "\u00c1guas",
                  "number": "F-1",
                  "account_reference": "CL-1",
                  "amount": "1500.00",
                  "paid": "0.00",
                  "open": "1500.00",
                  "currency": "MZN",
                  "issue_date": "2026-10-10",
                  "due_date": "2026-10-31",
                  "status": "open",
                  "expires_at": "2026-11-30T00:00:00Z",
                  "engagement": {
                    "opened": 0,
                    "pinned": 0
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "changeInvoice",
        "summary": "Change or cancel an invoice",
        "description": "The amount or due date of an unpaid invoice, or `status: cancelled`. A paid or partly paid invoice cannot be changed (`409 invoice_locked`). Requires `invoices:write`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "string",
                    "example": "1500.00"
                  },
                  "due_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "cancelled"
                    ]
                  }
                }
              },
              "example": {
                "status": "cancelled"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "number": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "paid": {
                      "type": "string"
                    },
                    "open": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "issue_date": {
                      "type": "string"
                    },
                    "due_date": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPlGyWnT4jx8g7CGpEoWP0GHe",
                  "biller": "AGUAS",
                  "biller_name": "\u00c1guas",
                  "number": "F-2",
                  "account_reference": "CL-2",
                  "amount": "900.00",
                  "paid": "0.00",
                  "open": "900.00",
                  "currency": "MZN",
                  "issue_date": "2026-10-10",
                  "due_date": "2026-10-31",
                  "status": "cancelled",
                  "expires_at": "2026-11-30T00:00:00Z"
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invoice_locked`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_locked",
                  "detail": "invoice F-1 is paid and cannot be changed",
                  "error": {
                    "code": "invoice_locked",
                    "message": "invoice F-1 is paid and cannot be changed"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payers": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "savePayer",
        "summary": "Register or update a payer",
        "description": "Your payer in KulPay's payer directory (BIL-5.4), under your own `external_id`: `201` created, `200` updated. Name, phone and email are sealed and never put in events; phone and email are matched, as keyed hashes, to billers' enrolments, which subscribe the payer (`bill_subscription.opted_in` with `origin: biller_enrolled`). Requires `bills:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "external_id"
                ],
                "properties": {
                  "external_id": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "name": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              },
              "example": {
                "external_id": "u-1",
                "name": "Ana",
                "phone": "+258841234567"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The payer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3K4mvOpRKqOU8KwHnmvdbsC0spb",
                  "external_id": "CLI-000482",
                  "name": "Ana Macu\u00e1cua",
                  "phone": "258841234567",
                  "email": "ana.macuacua@example.co.mz",
                  "created_at": "2026-06-14T08:30:00Z"
                }
              }
            }
          },
          "201": {
            "description": "The payer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPlDfQKeSRExAGpYcLFhGY2jR",
                  "external_id": "u-1",
                  "name": "Ana",
                  "phone": "258841234567",
                  "created_at": "2026-10-10T22:14:59.658576008Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "email is not an email address",
                  "error": {
                    "code": "invalid_request",
                    "message": "email is not an email address"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payers/{id}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getPayer",
        "summary": "A payer",
        "description": "Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The KulPay payer id or your external_id."
          }
        ],
        "responses": {
          "200": {
            "description": "The payer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "external_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "phone": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3Kpx6EPL2FOQLXxXg0PeZMIUgIx",
                  "external_id": "cust-000184",
                  "name": "Joana Cossa",
                  "phone": "+258841234567",
                  "email": "joana.cossa@example.co.mz",
                  "created_at": "2026-08-14T12:03:55Z"
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payers/{id}/subscriptions": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "subscribe",
        "summary": "Opt a payer in to a biller's bills",
        "description": "The payer chooses to receive a biller account's bills through your channel (BIL-5.4). `consent` is required: `accepted: true` and the `text_version` of the terms they accepted (`422 consent_required`); KulPay records it with the time and your channel. An account the biller can look up is verified first (`422 reference_not_found`; `pending_verification` while the biller is unreachable), and the customer's name is returned masked. Opting in again after opting out reactivates the subscription. Records `bill_subscription.opted_in`. Requires `bills:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The KulPay payer id or your external_id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "biller",
                  "account_reference",
                  "consent"
                ],
                "properties": {
                  "biller": {
                    "type": "string"
                  },
                  "account_reference": {
                    "type": "string"
                  },
                  "alias": {
                    "type": "string"
                  },
                  "consent": {
                    "type": "object",
                    "required": [
                      "accepted",
                      "text_version"
                    ],
                    "properties": {
                      "accepted": {
                        "type": "boolean"
                      },
                      "text_version": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "biller": "AGUAS",
                "account_reference": "CL-9",
                "alias": "Casa",
                "consent": {
                  "accepted": true,
                  "text_version": "2026-10"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The subscription (`active` or `pending_verification`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "alias": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "origin": {
                      "type": "string"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPlDuqLcP8vv2BFC9FcZqeDgd",
                  "biller": "AGUAS",
                  "biller_name": "\u00c1guas",
                  "account_reference": "CL-9",
                  "alias": "Casa",
                  "status": "active",
                  "origin": "payer",
                  "consent_version": "2026-10",
                  "consent_at": "2026-10-10T22:14:59.682786547Z"
                }
              }
            }
          },
          "404": {
            "description": "Unknown payer or biller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`consent_required`, `reference_not_found`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Content",
                  "status": 422,
                  "code": "consent_required",
                  "detail": "the payer's consent is required: consent.accepted and the version of the terms they accepted",
                  "error": {
                    "code": "consent_required",
                    "message": "the payer's consent is required: consent.accepted and the version of the terms they accepted"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listSubscriptions",
        "summary": "A payer's subscriptions",
        "description": "Every status: `active`, `pending_verification`, `opted_out`, `ended` (the biller removed the enrolment). Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The KulPay payer id or your external_id."
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "account_reference": {
                            "type": "string"
                          },
                          "alias": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "origin": {
                            "type": "string"
                          },
                          "consent_version": {
                            "type": "string"
                          },
                          "consent_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "opted_out_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlDuqLcP8vv2BFC9FcZqeDgd",
                      "biller": "AGUAS",
                      "biller_name": "\u00c1guas",
                      "account_reference": "CL-9",
                      "alias": "Casa",
                      "status": "opted_out",
                      "origin": "payer",
                      "consent_version": "2026-10",
                      "consent_at": "2026-10-10T22:14:59.682786547Z",
                      "opted_out_at": "2026-10-10T22:14:59.73153362Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/payers/{id}/subscriptions/{sid}": {
      "delete": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "optOut",
        "summary": "Opt a payer out",
        "description": "Withdraws the subscription at once (BIL-5.4): nothing of that biller account is delivered any more, and a later enrolment by the biller does not subscribe the payer again. Optional `?reason=`. Records `bill_subscription.opted_out`. Requires `bills:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The KulPay payer id or your external_id."
          },
          {
            "name": "sid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reason",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The subscription, `opted_out`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "alias": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "origin": {
                      "type": "string"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "opted_out_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPlDuqLcP8vv2BFC9FcZqeDgd",
                  "biller": "AGUAS",
                  "biller_name": "\u00c1guas",
                  "account_reference": "CL-9",
                  "alias": "Casa",
                  "status": "opted_out",
                  "origin": "payer",
                  "consent_version": "2026-10",
                  "consent_at": "2026-10-10T22:14:59.682786547Z",
                  "opted_out_at": "2026-10-10T22:14:59.73153362Z"
                }
              }
            }
          },
          "404": {
            "description": "Not this payer's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/payers/{id}/invoices": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "payerInvoices",
        "summary": "A payer's open bills",
        "description": "The open invoices of the billers the payer is subscribed to (`source: presented`, pay with `invoice_id`), and the amount due at lookup billers, asked now (`source: lookup`, pay with the `lookup_id` and `product`; `unavailable` when the biller does not answer) (BIL-5.6). New invoices arrive as `bill_invoice.delivered` webhooks, and `bill_invoice.reminded` three days before they are due. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The KulPay payer id or your external_id."
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "source": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "account_reference": {
                            "type": "string"
                          },
                          "alias": {
                            "type": "string"
                          },
                          "invoice": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "biller": {
                                "type": "string"
                              },
                              "biller_name": {
                                "type": "string"
                              },
                              "number": {
                                "type": "string"
                              },
                              "account_reference": {
                                "type": "string"
                              },
                              "amount": {
                                "type": "string"
                              },
                              "paid": {
                                "type": "string"
                              },
                              "open": {
                                "type": "string"
                              },
                              "currency": {
                                "type": "string"
                              },
                              "issue_date": {
                                "type": "string"
                              },
                              "due_date": {
                                "type": "string"
                              },
                              "status": {
                                "type": "string"
                              },
                              "expires_at": {
                                "type": "string",
                                "format": "date-time"
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "source": "presented",
                      "biller": "AGUAS",
                      "biller_name": "\u00c1guas",
                      "account_reference": "CL-9",
                      "alias": "Casa",
                      "invoice": {
                        "id": "3KWPlFgLlzNeKjlNMg1Xp9qGbkU",
                        "biller": "AGUAS",
                        "biller_name": "\u00c1guas",
                        "number": "F-9",
                        "account_reference": "CL-9",
                        "amount": "300.00",
                        "paid": "0.00",
                        "open": "300.00",
                        "currency": "MZN",
                        "issue_date": "2026-10-10",
                        "due_date": "2026-12-31",
                        "status": "open",
                        "expires_at": "2027-01-30T00:00:00Z"
                      }
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/payers": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "enrolPayers",
        "summary": "Enrol your customers",
        "description": "A biller registers its customers so they receive its bills on KulPay channels (BIL-5.5): `lawful_basis` (consent, contract, legal_obligation, legitimate_interest) and up to 1,000 `payers` with an account reference and a phone number or email. Each is matched to channels' payers by phone or email; matched payers are subscribed (their channel is told and can let them opt out), except those who opted out of that account. Answers per customer with `payers_subscribed`. Requires `payers:write`. With `lawful_basis: consent`, each payer carries its consent (consent_at, consent_channel, consent_text), kept with the enrolment and listed by GET /v1/biller/payers; a payer without it is rejected on its line. The portal's billing page enrols the same way, one customer or a CSV (F3-13). Each payer can carry `language` (pt, the default, or en) for the invoice notices KulPay sends when the biller switches them on (PUT /v1/biller/notices, F3-15).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lawful_basis",
                  "payers"
                ],
                "properties": {
                  "lawful_basis": {
                    "type": "string",
                    "enum": [
                      "consent",
                      "contract",
                      "legal_obligation",
                      "legitimate_interest"
                    ]
                  },
                  "payers": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "object",
                      "required": [
                        "account_reference"
                      ],
                      "properties": {
                        "account_reference": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "consent_at": {
                          "type": "string",
                          "description": "When the payer consented: YYYY-MM-DD or RFC 3339. Required with lawful_basis consent (F3-13)."
                        },
                        "consent_channel": {
                          "type": "string",
                          "enum": [
                            "portal",
                            "web",
                            "app",
                            "ussd",
                            "sms",
                            "call",
                            "paper",
                            "counter"
                          ],
                          "description": "Where the payer consented."
                        },
                        "consent_text": {
                          "type": "string",
                          "maxLength": 1000,
                          "description": "The text the payer accepted."
                        },
                        "language": {
                          "type": "string",
                          "enum": [
                            "pt",
                            "en"
                          ],
                          "description": "Language of the invoice notices KulPay sends the payer (F3-15); pt by default."
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "lawful_basis": "contract",
                "payers": [
                  {
                    "account_reference": "CL-9",
                    "phone": "841234567"
                  },
                  {
                    "account_reference": "CL-10",
                    "phone": "841234567"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{results}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "account_reference": {
                            "type": "string"
                          },
                          "result": {
                            "type": "string"
                          },
                          "payers_subscribed": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "results": [
                    {
                      "account_reference": "CL-9",
                      "result": "enrolled",
                      "payers_subscribed": 0
                    },
                    {
                      "account_reference": "CL-10",
                      "result": "enrolled",
                      "payers_subscribed": 1
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "`not_a_biller`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "not_a_biller",
                  "detail": "this key's organisation is not a biller on KulPay",
                  "error": {
                    "code": "not_a_biller",
                    "message": "this key's organisation is not a biller on KulPay"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listEnrolments",
        "summary": "Your enrolled customers",
        "description": "Contacts masked. `notices_stopped_at` is when the payer stopped the invoice notices KulPay sends (STOP by SMS or the email link, F3-15): enrolling them again does not undo it. Requires `payers:write`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "account_reference": {
                            "type": "string"
                          },
                          "phone_masked": {
                            "type": "string"
                          },
                          "lawful_basis": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "consent_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "consent_channel": {
                            "type": "string"
                          },
                          "consent_text": {
                            "type": "string"
                          },
                          "language": {
                            "type": "string",
                            "enum": [
                              "pt",
                              "en"
                            ]
                          },
                          "notices_stopped_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "account_reference": "CL-10",
                      "phone_masked": "*****4567",
                      "lawful_basis": "contract",
                      "created_at": "2026-10-10T22:14:59.775211958Z"
                    },
                    {
                      "account_reference": "CL-9",
                      "phone_masked": "*****4567",
                      "lawful_basis": "contract",
                      "created_at": "2026-10-10T22:14:59.755120163Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/payers/{ref}": {
      "delete": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "unenrol",
        "summary": "Remove an enrolment",
        "description": "The subscriptions it created end (`bill_subscription.ended`) and its data is deleted after 30 days. Requires `payers:write`.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The account reference."
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No active enrolment.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/remittances": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminListRemittances",
        "summary": "Remittances to billers",
        "description": "Newest first, of one `biller` or all. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "biller",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "gross_minor": {
                            "type": "integer"
                          },
                          "fee_minor": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          },
                          "period_from": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "period_to": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "paying",
                              "paid"
                            ]
                          },
                          "bank_reference": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "paid_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "payout_id": {
                            "type": "string",
                            "description": "The bank transfer that pays it by the rail (F2-4)."
                          },
                          "paid_by": {
                            "type": "string",
                            "enum": [
                              "bank_rail",
                              "manual",
                              "simulation"
                            ]
                          },
                          "adjustment_minor": {
                            "type": "integer",
                            "description": "Deducted for payments reversed after an earlier remittance paid them (F3-16): net = gross - fee - adjustment."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                      "mode": "test",
                      "currency": "MZN",
                      "count": 1,
                      "gross_minor": 150000,
                      "fee_minor": 0,
                      "net_minor": 150000,
                      "period_from": "2026-10-10T22:14:59.495269929Z",
                      "period_to": "2026-10-10T22:14:59.495269929Z",
                      "status": "paid",
                      "bank_reference": "BCI-1",
                      "approval_id": "3KWPlDhVsFz6D7Nlw0sljeII8l1",
                      "created_at": "2026-10-10T22:14:59.538329669Z",
                      "paid_at": "2026-10-10T22:14:59.591566406Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/remittances": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "remitBiller",
        "summary": "Remit a biller now",
        "description": "Creates the biller's remittances of what KulPay collected for it on the agent model and has not remitted, less the fees it bears, one per mode and currency (BIL-5.7). Billers with a bank account and a `remit_frequency` are remitted daily or weekly by job. Records `bill_remittance.created` (a webhook to the biller). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`: the remittances created, none when nothing is owed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "gross_minor": {
                            "type": "integer"
                          },
                          "fee_minor": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          },
                          "period_from": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "period_to": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "adjustment_minor": {
                            "type": "integer",
                            "description": "Deducted for payments reversed after an earlier remittance paid them (F3-16): net = gross - fee - adjustment."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                      "mode": "test",
                      "currency": "MZN",
                      "count": 1,
                      "gross_minor": 150000,
                      "fee_minor": 0,
                      "net_minor": 150000,
                      "period_from": "2026-10-10T22:14:59.495269929Z",
                      "period_to": "2026-10-10T22:14:59.495269929Z",
                      "status": "pending",
                      "created_at": "2026-10-10T22:14:59.538329669Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/remittances/{id}/paid": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "remittancePaid",
        "summary": "Record a remittance paid by hand",
        "description": "For a bank without files: the transfer treasury made by hand, its reference and a reason. A second person approves; the remittance is marked `paid_by: manual`, the biller payable is cleared against client funds and the biller receives `bill_remittance.paid`. A remittance whose transfer is in the bank rail (`paying`) cannot be recorded by hand. Prefer POST /v1/admin/remittances/{id}/pay. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "bank_reference",
                  "reason"
                ],
                "properties": {
                  "bank_reference": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "bank_reference": "BCI-1",
                "reason": "daily"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPlDhVsFz6D7Nlw0sljeII8l1",
                    "kind": "bill.remittance_paid",
                    "subject_id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                    "tenant_id": "platform",
                    "payload": "{\"bank_reference\":\"BCI-1\"}",
                    "reason": "daily",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:14:59.570339309Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Already paid, or no reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the remittance is already paid",
                  "error": {
                    "code": "invalid_request",
                    "message": "the remittance is already paid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/remittances/{id}/report.csv": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminRemittanceReport",
        "summary": "A remittance report",
        "description": "Every payment remitted: KulPay id, date, invoice number, account reference, amount, fee, net; and the total. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/remittances": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerRemittances",
        "summary": "Your remittances",
        "description": "What KulPay remitted, or will remit, to you. Requires `invoices:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "gross_minor": {
                            "type": "integer"
                          },
                          "fee_minor": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          },
                          "period_from": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "period_to": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "adjustment_minor": {
                            "type": "integer",
                            "description": "Deducted for payments reversed after an earlier remittance paid them (F3-16): net = gross - fee - adjustment."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                      "mode": "test",
                      "currency": "MZN",
                      "count": 1,
                      "gross_minor": 150000,
                      "fee_minor": 0,
                      "net_minor": 150000,
                      "period_from": "2026-10-10T22:14:59.495269929Z",
                      "period_to": "2026-10-10T22:14:59.495269929Z",
                      "status": "pending",
                      "created_at": "2026-10-10T22:14:59.538329669Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/remittances/{id}/report.csv": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerRemittanceReport",
        "summary": "Your remittance report",
        "description": "As the operators see it, for reconciling your customers' accounts. Requires `invoices:read`. Payments reversed after an earlier remittance paid them (F3-16) appear as negative lines marked `(reversed)` and are deducted from the total; such an invoice was reopened with `bill_invoice.reopened`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Not yours.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoice-files": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "loadInvoiceFile",
        "summary": "Upload an invoice file",
        "description": "A biller that presents by file sends a CSV (BIL-5.3) with the header `invoice_number, account_reference, amount, currency, issue_date, due_date, description` (issue_date and description optional), at most 10,000 rows and 5 MB. The file is checked as a whole, then loaded row by row as `POST /v1/biller/invoices` would; loading it again updates the unpaid invoices rather than duplicating them. Answers with the counts and a result per row, or the result file with `?format=csv`. Records `bill_invoice.file_loaded`. Requires `invoices:write`.",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "csv"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/csv": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{sha256, rows, created, updated, rejected, results}`, or the result CSV.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sha256": {
                      "type": "string"
                    },
                    "rows": {
                      "type": "integer"
                    },
                    "created": {
                      "type": "integer"
                    },
                    "updated": {
                      "type": "integer"
                    },
                    "rejected": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "number": {
                            "type": "string"
                          },
                          "result": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "sha256": "90868a50e8b9c3b766597f54aa00e65c587cda5f7454803e5e8a4fb38d9fad00",
                  "rows": 2,
                  "created": 1,
                  "updated": 0,
                  "rejected": 1,
                  "results": [
                    {
                      "number": "G-1",
                      "result": "created",
                      "invoice_id": "3KWPlG2Ons62fPH0IBShlpPJm9n"
                    },
                    {
                      "number": "G-2",
                      "result": "rejected",
                      "error": "amount must be a positive decimal"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Not a valid file, or not set up to present by file.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the header must have invoice_number, account_reference, amount, currency, issue_date, due_date, description",
                  "error": {
                    "code": "invalid_request",
                    "message": "the header must have invoice_number, account_reference, amount, currency, issue_date, due_date, description"
                  }
                }
              }
            }
          },
          "413": {
            "description": "`file_too_large`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Content Too Large",
                  "status": 413,
                  "code": "file_too_large",
                  "detail": "an invoice file is at most 5 MB",
                  "error": {
                    "code": "file_too_large",
                    "message": "an invoice file is at most 5 MB"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/products/{product}/limits": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "requestBillLimits",
        "summary": "Change a product's daily limits",
        "description": "Per day (Maputo time): payments to one customer reference (`reference_daily_count`, `reference_daily_minor`) and the amount one channel sells (`channel_daily_minor`); 0 removes a limit (BIL-6.4). A second operator approves. Quotes and payments beyond a limit are refused with `422 limit_exceeded`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reference_daily_count": {
                    "type": "integer"
                  },
                  "reference_daily_minor": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "channel_daily_minor": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reference_daily_count": 3,
                "channel_daily_minor": 1000000,
                "reason": "policy"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPlO70sHMc9BFjsbRiot7VUEk",
                    "kind": "bill.limits",
                    "subject_id": "3KWPlDIPesBSi4VERFPanmdB27f",
                    "tenant_id": "platform",
                    "payload": "{\"reference_daily_count\":3,\"reference_daily_minor\":0,\"channel_daily_minor\":1000000}",
                    "reason": "policy",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:00.011523819Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown product.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/commission-plans": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "proposeCommissionPlan",
        "summary": "Propose a commission plan",
        "description": "What a channel earns (`we_pay`) or pays (`we_charge`) per bill payment it sells (BIL-3.3): for `channel_id` or all channels, a biller's `product` or all, `fixed_minor` plus `bps`, optionally `tiers` [{from_minor, bps}] by the channel's volume this month, from `effective_from`. A second person activates it (the plan it replaces for the same scope ends). Payments accrue against the plan in force when quoted, when they succeed (`bill_commission.accrued`), credited to or debited from the channel's balance. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "direction",
                  "reason"
                ],
                "properties": {
                  "channel_id": {
                    "type": "string"
                  },
                  "biller": {
                    "type": "string"
                  },
                  "product": {
                    "type": "string"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "we_pay",
                      "we_charge"
                    ]
                  },
                  "fixed_minor": {
                    "type": "integer",
                    "format": "int64"
                  },
                  "bps": {
                    "type": "integer"
                  },
                  "tiers": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "from_minor": {
                          "type": "integer",
                          "format": "int64"
                        },
                        "bps": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "effective_from": {
                    "type": "string",
                    "format": "date"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "channel_id": "3KWPl9fd5z3WDdnaGzpvnzDkLUW",
                "direction": "we_pay",
                "bps": 150,
                "reason": "agents"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The plan, `pending`, with its `approval_id`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "channel_id": {
                      "type": "string"
                    },
                    "direction": {
                      "type": "string"
                    },
                    "bps": {
                      "type": "integer"
                    },
                    "effective_from": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "status": {
                      "type": "string"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPlHvsSlANI9980QIV6EKj9y4",
                  "channel_id": "3KWPl9fd5z3WDdnaGzpvnzDkLUW",
                  "direction": "we_pay",
                  "bps": 150,
                  "effective_from": "2026-10-10T22:14:59.95764134Z",
                  "status": "pending",
                  "version": 1,
                  "approval_id": "3KWPlGFxMcmtqjHW45BKc6Jqgkq",
                  "created_by": "key_accounts",
                  "created_at": "2026-10-10T22:14:59.95764134Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "direction is we_pay or we_charge",
                  "error": {
                    "code": "invalid_request",
                    "message": "direction is we_pay or we_charge"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listCommissionPlans",
        "summary": "Commission plans",
        "description": "Of a `channel` (and the plans for all channels) or all. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "channel_id": {
                            "type": "string"
                          },
                          "direction": {
                            "type": "string"
                          },
                          "bps": {
                            "type": "integer"
                          },
                          "effective_from": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPlHvsSlANI9980QIV6EKj9y4",
                      "channel_id": "3KWPl9fd5z3WDdnaGzpvnzDkLUW",
                      "direction": "we_pay",
                      "bps": 150,
                      "effective_from": "2026-10-10T22:14:59.95764134Z",
                      "status": "active",
                      "version": 1,
                      "approval_id": "3KWPlGFxMcmtqjHW45BKc6Jqgkq",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:14:59.95764134Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-commissions": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "commissionStatement",
        "summary": "Your commission statement",
        "description": "Your bill sales and commission for a `month` (YYYY-MM, Maputo time; this month by default), each sale with its commission. Requires `bills:read`.",
        "parameters": [
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "2026-10"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{month, sales, volume, commission, lines}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel_id": {
                      "type": "string"
                    },
                    "month": {
                      "type": "string"
                    },
                    "sales": {
                      "type": "integer"
                    },
                    "volume": {
                      "type": "string"
                    },
                    "commission": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "completed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "commission": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "channel_id": "3KWPl9fd5z3WDdnaGzpvnzDkLUW",
                  "month": "2026-10",
                  "sales": 1,
                  "volume": "1500.00",
                  "commission": "0.00",
                  "currency": "MZN",
                  "lines": [
                    {
                      "bill_payment_id": "3KWPlDKa4bRtfAl70HOzBdgc2WY",
                      "completed_at": "2026-10-10T22:14:59.495269929Z",
                      "biller": "AGUAS",
                      "product": "FACTURA",
                      "amount": "1500.00",
                      "commission": "0.00"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-commissions": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminCommissionStatement",
        "summary": "A channel's commission statement",
        "description": "As the channel sees it, for `channel` and `month`. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "channel",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The statement.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel_id": {
                      "type": "string"
                    },
                    "month": {
                      "type": "string"
                    },
                    "sales": {
                      "type": "integer"
                    },
                    "volume": {
                      "type": "string"
                    },
                    "commission": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "completed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "commission": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "channel_id": "3K4SjgBC4GZKqZAEojB1QDdbYiw",
                  "month": "2026-09",
                  "sales": 2,
                  "volume": "1500.00",
                  "commission": "22.50",
                  "currency": "MZN",
                  "lines": [
                    {
                      "bill_payment_id": "3K7sTNTzRrMVuuCSlaWe6IT5tcr",
                      "completed_at": "2026-09-12T08:31:05Z",
                      "biller": "edm",
                      "product": "credelec",
                      "amount": "1000.00",
                      "commission": "15.00"
                    },
                    {
                      "bill_payment_id": "3K2xMacVPsuM57QuDBSLsOiRYpc",
                      "completed_at": "2026-09-21T18:02:47Z",
                      "biller": "aguas_maputo",
                      "product": "water",
                      "amount": "500.00",
                      "commission": "7.50"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/supply-connectors/{name}/reconcile": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "reconcileSupply",
        "summary": "Reconcile a day with a supplier",
        "description": "Compares the payments KulPay sent the connector on `day` (YYYY-MM-DD, UTC; yesterday by default) with the supplier's records (BIL-6.3): its listing (EDM INT10, the biller contract's GET /payments?date=), or, with a `text/csv` body, its report file (columns request_id, supplier_reference, customer_reference, amount). Payments match by request id, else by receipt. Breaks: supplier_only, kulpay_only, amount_mismatch, review_found (the supplier delivered a payment KulPay has pending or in review), failed_but_delivered. Runs daily for connectors that list their payments (`bill_recon.completed`, `bill_recon.break_found`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "day",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "text/csv": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The run with its breaks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "connector_id": {
                      "type": "string"
                    },
                    "day": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "source": {
                      "type": "string"
                    },
                    "supplier_count": {
                      "type": "integer"
                    },
                    "kulpay_count": {
                      "type": "integer"
                    },
                    "matched": {
                      "type": "integer"
                    },
                    "breaks": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "connector": {
                      "type": "string"
                    },
                    "breaks_list": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "supplier_reference": {
                            "type": "string"
                          },
                          "customer_reference": {
                            "type": "string"
                          },
                          "supplier_amount_minor": {
                            "type": "integer"
                          },
                          "kulpay_amount_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KUYI4ETn1uGx7HJyAaNr2PlCv1",
                  "connector_id": "3KUXT4pDy6eRf8sUhJkYc2AwMg6",
                  "day": "2026-10-10T00:00:00Z",
                  "source": "file",
                  "supplier_count": 1287,
                  "kulpay_count": 1288,
                  "matched": 1286,
                  "breaks": 2,
                  "created_at": "2026-10-11T06:00:41Z",
                  "connector": "edm_live",
                  "breaks_list": [
                    {
                      "kind": "amount_mismatch",
                      "bill_payment_id": "3KUYJ7FUo4vHy1IKzBbOs5QmDw2",
                      "request_id": "3KUYK1GVp7wIz4JLACcPt8RnEx3",
                      "supplier_reference": "EDM-20261010-558213",
                      "customer_reference": "07012345678",
                      "supplier_amount_minor": 50000,
                      "kulpay_amount_minor": 100000
                    },
                    {
                      "kind": "kulpay_only",
                      "bill_payment_id": "3KUYL4HWq1xJA7KMBDdQu2SoFy4",
                      "request_id": "3KUYM7IXr4yKB1LNCEeRv5TpGz5",
                      "supplier_reference": "EDM-20261010-558990",
                      "customer_reference": "07019876543",
                      "kulpay_amount_minor": 25000
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The driver cannot list its payments, or a bad file.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the header needs amount and request_id or supplier_reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "the header needs amount and request_id or supplier_reference"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown connector.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-recon": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listReconRuns",
        "summary": "Supplier reconciliation runs",
        "description": "Recent runs with their breaks, of a `connector` or all. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "connector",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "connector_id": {
                            "type": "string"
                          },
                          "day": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "source": {
                            "type": "string"
                          },
                          "supplier_count": {
                            "type": "integer"
                          },
                          "kulpay_count": {
                            "type": "integer"
                          },
                          "matched": {
                            "type": "integer"
                          },
                          "breaks": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "connector": {
                            "type": "string"
                          },
                          "breaks_list": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string"
                                },
                                "request_id": {
                                  "type": "string"
                                },
                                "supplier_reference": {
                                  "type": "string"
                                },
                                "customer_reference": {
                                  "type": "string"
                                },
                                "supplier_amount_minor": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUYI4ETn1uGx7HJyAaNr2PlCv1",
                      "connector_id": "3KUXT4pDy6eRf8sUhJkYc2AwMg6",
                      "day": "2026-10-10T00:00:00Z",
                      "source": "pull",
                      "supplier_count": 1287,
                      "kulpay_count": 1287,
                      "matched": 1286,
                      "breaks": 1,
                      "created_at": "2026-10-11T06:00:41Z",
                      "connector": "edm_live",
                      "breaks_list": [
                        {
                          "kind": "supplier_only",
                          "request_id": "3KUZD4ZOI1PBS7CETVvIM2KGgQ2",
                          "supplier_reference": "EDM-20261010-559102",
                          "customer_reference": "07011122233",
                          "supplier_amount_minor": 50000
                        }
                      ]
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/logos/{id}": {
      "get": {
        "tags": [
          "Partners"
        ],
        "operationId": "getPartnerLogo",
        "summary": "A partner's logo",
        "description": "The approved logo of a PSP or biller: a PNG with a transparent background, public (no key), for every channel to show. The biller catalogue gives its address as `logo_url`, with `?v=<version>`: a new logo is a new URL, and a versioned request may be cached for good (`Cache-Control: immutable`). `ETag` and `If-None-Match` are supported.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The partner (PSP) id."
          },
          {
            "name": "v",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The logo version."
          }
        ],
        "responses": {
          "200": {
            "description": "The logo.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Not modified."
          },
          "404": {
            "description": "No approved logo.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                },
                "example": "404 page not found\n"
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/logo": {
      "get": {
        "tags": [
          "Partners"
        ],
        "operationId": "adminGetPartnerLogo",
        "summary": "A partner's logo, published or in review",
        "description": "`?pending=1` returns the logo waiting for review. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The partner (PSP) id."
          },
          {
            "name": "pending",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The logo.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "None.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such logo",
                  "error": {
                    "code": "not_found",
                    "message": "no such logo"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Partners"
        ],
        "operationId": "adminPutPartnerLogo",
        "summary": "Publish a partner's logo",
        "description": "An operator publishes a logo at once (a partner uploads its own in the portal, for review). The body is the PNG: an alpha channel, transparent edges (all four corners and at least 90% of the border), 256 to 2048 px a side, aspect ratio 1:4 to 4:1, at most 1 MB. Anything else is refused with `400 invalid_logo` saying what to fix. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The partner (PSP) id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "image/png": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The logo state: `logo_status`, `logo_url`, `pending`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "logo_status": {
                      "type": "string"
                    },
                    "logo_note": {},
                    "logo_url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "logo_status": "approved",
                  "logo_note": null,
                  "logo_url": "/logos/3KARihnYbNSj5SoAHKBQV7Bw0uO?v=3KWwi7owalkXqkSxnppSk2sKLoe"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_logo`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_logo",
                  "detail": "the logo must have a transparent background: its corners are opaque",
                  "error": {
                    "code": "invalid_logo",
                    "message": "the logo must have a transparent background: its corners are opaque"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown partner.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such partner or logo",
                  "error": {
                    "code": "not_found",
                    "message": "no such partner or logo"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psps/{id}/logo/review": {
      "post": {
        "tags": [
          "Partners"
        ],
        "operationId": "adminReviewPartnerLogo",
        "summary": "Approve or reject a partner's logo",
        "description": "Approving publishes the logo waiting for review; rejecting needs a `note` saying why, and keeps the previously approved logo published. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The partner (PSP) id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "approve"
                ],
                "properties": {
                  "approve": {
                    "type": "boolean"
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 255
                  }
                }
              },
              "example": {
                "approve": true,
                "note": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The logo state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "psp_id": {
                      "type": "string"
                    },
                    "logo_status": {
                      "type": "string"
                    },
                    "logo_note": {
                      "type": "string"
                    },
                    "logo_url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "psp_id": "3KARihnYbNSj5SoAHKBQV7Bw0uO",
                  "logo_status": "rejected",
                  "logo_note": "The logo is cropped on the right; send the full mark with some margin.",
                  "logo_url": "/logos/3KARihnYbNSj5SoAHKBQV7Bw0uO?v=3KWwi7owalkXqkSxnppSk2sKLoe"
                }
              }
            }
          },
          "400": {
            "description": "Nothing to review, or a rejection without a note.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_logo",
                  "detail": "say why the logo is rejected, so the partner can fix it",
                  "error": {
                    "code": "invalid_logo",
                    "message": "say why the logo is rejected, so the partner can fix it"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices/{number}/document": {
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putInvoiceDocument",
        "summary": "Attach an invoice's document",
        "description": "The body is the PDF (`Content-Type: application/pdf`, starting `%PDF-`, at most 5 MB); `?filename=` names it. Replaces any previous document. A cancelled or expired invoice takes none (`409 invoice_closed`). Channels download it from the gateway. Requires `invoices:write`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          },
          {
            "name": "filename",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/pdf": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The invoice, with its `document`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "number": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "paid": {
                      "type": "string"
                    },
                    "open": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "issue_date": {
                      "type": "string"
                    },
                    "due_date": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "document": {
                      "type": "object",
                      "properties": {
                        "filename": {
                          "type": "string"
                        },
                        "size": {
                          "type": "integer"
                        },
                        "content_type": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KnE2odsld9qpNsTIAS3N9RyYRi",
                  "biller": "AGUA-LIMPA",
                  "biller_name": "\u00c1gua Limpa, Lda",
                  "number": "FT-2026-00123",
                  "account_reference": "100234567",
                  "amount": "1250.00",
                  "paid": "0.00",
                  "open": "1250.00",
                  "currency": "MZN",
                  "issue_date": "2026-10-01",
                  "due_date": "2026-10-31",
                  "description": "Consumo de \u00e1gua, setembro de 2026",
                  "status": "open",
                  "expires_at": "2026-11-30T00:00:00Z",
                  "document": {
                    "filename": "FT-2026-00123.pdf",
                    "size": 84213,
                    "content_type": "application/pdf",
                    "url": "/v1/biller/invoices/FT-2026-00123/document"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a PDF, or empty.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the document must be a PDF",
                  "error": {
                    "code": "invalid_request",
                    "message": "the document must be a PDF"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown invoice.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invoice_closed`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice FT-2026-00123 is paid: it takes no document",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice FT-2026-00123 is paid: it takes no document"
                  }
                }
              }
            }
          },
          "413": {
            "description": "`file_too_large`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Content Too Large",
                  "status": 413,
                  "code": "file_too_large",
                  "detail": "an invoice document is at most 5 MB",
                  "error": {
                    "code": "file_too_large",
                    "message": "an invoice document is at most 5 MB"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getInvoiceDocument",
        "summary": "Download an invoice's document",
        "description": "Requires `invoices:read`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF, as an attachment.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No document.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "deleteInvoiceDocument",
        "summary": "Remove an invoice's document",
        "description": "Requires `invoices:write`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No document.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-invoices/{id}/document": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "channelInvoiceDocument",
        "summary": "Download a bill's document",
        "description": "The document a biller attached to an invoice, for a channel that has a payer subscribed to that account or paid that invoice; anyone else gets `404`. The payer's invoice list (`GET /v1/payers/{id}/invoices`) gives this path as `document.url`. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice id."
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF, as an attachment.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No such document for this channel.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such invoice document",
                  "error": {
                    "code": "not_found",
                    "message": "no such invoice document"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-payments/{id}/receipt.pdf": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billReceiptPDF",
        "summary": "A printable receipt",
        "description": "The channel's own bill payment as a PDF to keep or print: biller, product, reference, amounts, dates, status, the KulPay and supplier references, and the tokens and PINs with their units and serials. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The bill payment id."
          }
        ],
        "responses": {
          "200": {
            "description": "The PDF, as an attachment.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "Unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices/{number}/events": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "invoiceTimeline",
        "summary": "An invoice's timeline",
        "description": "Every event the biller was sent about one of its invoices, oldest first, exactly as its webhooks carried them: `data.invoice` (the invoice after the event, the same in every event) and `data.details` (what is specific to the event). The biller-facing invoice events are `bill_invoice.presented`, `bill_invoice.updated`, `bill_invoice.cancelled`, `bill_invoice.expired`, `bill_invoice.overdue`, `bill_invoice.document_attached`, `bill_invoice.document_removed`, `bill_invoice.sent`, `bill_invoice.reminder_sent`, `bill_invoice.opened`, `bill_invoice.pinned`, `bill_invoice.unpinned`, `bill_invoice.payment_received`, `bill_invoice.partially_paid`, `bill_invoice.paid`, `bill_invoice.remitted`. Payment events name their participants and roles: `initiating_psp` (the channel paid through), `from_psp` (where the money came from: the channel's prefunded balance, or the payer's wallet or bank) and `to_psp` (where it goes for the biller: its bank by remittance, or KulPay for supplier-float products). Payers are named only by `payer_ref`, an opaque id stable per biller and payer. Delivery is at least once and may be out of order: process each event `id` once, and order an invoice's events by `sequence`. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "`{data: [{id, type, sequence, created_at, data}], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "sequence": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "data": {
                            "type": "object",
                            "properties": {
                              "details": {
                                "type": "object",
                                "properties": {
                                  "has_document": {
                                    "type": "boolean"
                                  },
                                  "lines_count": {
                                    "type": "integer"
                                  },
                                  "source": {
                                    "type": "string"
                                  }
                                }
                              },
                              "invoice": {
                                "type": "object",
                                "properties": {
                                  "account_reference": {
                                    "type": "string"
                                  },
                                  "amount": {
                                    "type": "string"
                                  },
                                  "biller": {
                                    "type": "string"
                                  },
                                  "currency": {
                                    "type": "string"
                                  },
                                  "due_date": {
                                    "type": "string"
                                  },
                                  "id": {
                                    "type": "string"
                                  },
                                  "issue_date": {
                                    "type": "string"
                                  },
                                  "number": {
                                    "type": "string"
                                  },
                                  "open": {
                                    "type": "string"
                                  },
                                  "paid": {
                                    "type": "string"
                                  },
                                  "status": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KM4b0MSzyoNnAgn5pbS9xwrBQo",
                      "type": "bill_invoice.presented",
                      "sequence": 1,
                      "created_at": "2026-10-01T06:00:12Z",
                      "data": {
                        "details": {
                          "has_document": false,
                          "lines_count": 0,
                          "source": "push"
                        },
                        "invoice": {
                          "account_reference": "100234567",
                          "amount": "1250.00",
                          "biller": "AGUA-LIMPA",
                          "currency": "MZN",
                          "due_date": "2026-10-31",
                          "id": "3KnE2odsld9qpNsTIAS3N9RyYRi",
                          "issue_date": "2026-10-01",
                          "number": "FT-2026-00123",
                          "open": "1250.00",
                          "paid": "0.00",
                          "status": "open"
                        }
                      }
                    },
                    {
                      "id": "3KjRgUjLWIWhBmLa6Z9fWbpx2xe",
                      "type": "bill_invoice.document_attached",
                      "sequence": 2,
                      "created_at": "2026-10-01T06:02:40Z",
                      "data": {
                        "details": {
                          "file_id": "3KRlP4WSt8uUE2E2OYdHrWSK4QF",
                          "filename": "FT-2026-00123.pdf",
                          "size": 84213
                        },
                        "invoice": {
                          "account_reference": "100234567",
                          "amount": "1250.00",
                          "biller": "AGUA-LIMPA",
                          "currency": "MZN",
                          "due_date": "2026-10-31",
                          "id": "3KnE2odsld9qpNsTIAS3N9RyYRi",
                          "issue_date": "2026-10-01",
                          "number": "FT-2026-00123",
                          "open": "1250.00",
                          "paid": "0.00",
                          "status": "open"
                        }
                      }
                    }
                  ],
                  "total": 2
                }
              }
            }
          },
          "404": {
            "description": "Unknown invoice.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-invoices/{id}/engagements": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "recordEngagement",
        "summary": "Report what a payer did with a bill",
        "description": "A channel reports that its payer opened, pinned or unpinned an invoice. The biller is told of a payer's first open (`bill_invoice.opened`) and of a pin when it changes (`bill_invoice.pinned`, `bill_invoice.unpinned`); repeats are acknowledged with `recorded: false` and change nothing. The payer must be subscribed to the invoice's account through this channel; the biller never learns who they are, only an opaque `payer_ref`. Requires `bills:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "payer_id"
                ],
                "properties": {
                  "type": {
                    "enum": [
                      "opened",
                      "pinned",
                      "unpinned"
                    ]
                  },
                  "payer_id": {
                    "type": "string",
                    "description": "Your payer (id or external_id)."
                  },
                  "surface": {
                    "enum": [
                      "app",
                      "web",
                      "ussd",
                      "agent",
                      "other"
                    ],
                    "default": "app"
                  }
                }
              },
              "example": {
                "type": "opened",
                "payer_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "surface": "app"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nothing new (`recorded: false`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "recorded": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "type": "opened",
                  "recorded": false
                }
              }
            }
          },
          "201": {
            "description": "Recorded: the biller is told.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "recorded": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "type": "pinned",
                  "recorded": true
                }
              }
            }
          },
          "400": {
            "description": "Unknown type or surface, or the payer is not subscribed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the payer is not subscribed to this invoice's account",
                  "error": {
                    "code": "invalid_request",
                    "message": "the payer is not subscribed to this invoice's account"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown invoice or payer.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "tags": [
          "Reference"
        ],
        "operationId": "oauthToken",
        "summary": "OAuth 2.0 token (client credentials)",
        "description": "RFC 6749 \u00a74.4 (PSP-8.7). The client authenticates with HTTP Basic or the form (`client_id`, `client_secret`), or a private-key JWT (RFC 7523: `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`, RS256 or ES256, `iss`=`sub`=client id, `aud` this endpoint's URL, `exp` within 5 minutes, single-use `jti`). The access token lives 15 minutes and carries the client's scopes, or the `scope` subset asked for; use it as a Bearer token. Errors follow RFC 6749 \u00a75.2.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type"
                ],
                "properties": {
                  "grant_type": {
                    "enum": [
                      "client_credentials"
                    ]
                  },
                  "scope": {
                    "type": "string"
                  },
                  "client_id": {
                    "type": "string"
                  },
                  "client_secret": {
                    "type": "string"
                  },
                  "client_assertion_type": {
                    "type": "string"
                  },
                  "client_assertion": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{access_token, token_type: \"Bearer\", expires_in: 900, scope}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "access_token": "kpat_\u2026",
                  "token_type": "Bearer",
                  "expires_in": 900,
                  "scope": "events:read keys:manage"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`, `invalid_scope`, `unsupported_grant_type`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "invalid_scope",
                  "error_description": "the client may not request payouts:write"
                }
              }
            }
          },
          "401": {
            "description": "`invalid_client`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "invalid_client",
                  "error_description": "client authentication failed"
                }
              }
            }
          }
        }
      }
    },
    "/v1/oauth-clients": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "createOAuthClient",
        "summary": "Register an OAuth client",
        "description": "`auth_method` `client_secret` (the secret is returned once) or `private_key_jwt` with `public_key_pem` (RSA 2048+ or EC P-256). Records `credential.oauth_client.created`. Requires `keys:manage`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "auth_method": {
                    "enum": [
                      "client_secret",
                      "private_key_jwt"
                    ]
                  },
                  "public_key_pem": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Electricidade de Mo\u00e7ambique",
                "scopes": [
                  "payments:write"
                ],
                "auth_method": "client_secret",
                "public_key_pem": "string"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The client (with `client_secret` once).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "client_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "auth_method": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "client_secret": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "client_id": "kpc_live_oWrrIRg2KYzsZ1uZSo",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "name": "ERP integration",
                  "auth_method": "client_secret",
                  "scopes": [
                    "payments:read",
                    "payments:write"
                  ],
                  "created_at": "2026-10-11T09:00:00Z",
                  "client_secret": "kpcs_\u2026"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listOAuthClients",
        "summary": "The account's OAuth clients",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "client_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "auth_method": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "client_id": "kpc_test_H7kZ4EnTTL2ahZTISVVx8Ihh",
                      "account_id": "3KWPnZCUez2DUwXC8OzDDkOJnkE",
                      "mode": "test",
                      "name": "bank",
                      "auth_method": "client_secret",
                      "scopes": [
                        "events:read",
                        "keys:manage"
                      ],
                      "created_at": "2026-10-10T22:15:18.799340317Z",
                      "last_used_at": "2026-10-10T22:15:18.826586951Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/oauth-clients/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "revokeOAuthClient",
        "summary": "Revoke an OAuth client",
        "description": "Its tokens stop working at once.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          }
        }
      }
    },
    "/v1/client-certificates": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "issueClientCertificate",
        "summary": "Issue a client certificate from a CSR",
        "description": "The region's private CA signs a client certificate valid 13 months, bound to the account (`credential.certificate.issued`). Reminders 30, 7 and 1 days before expiry; issue the next one before then \u2014 both work until the old one expires. Requires `keys:manage`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "csr"
                ],
                "properties": {
                  "csr": {
                    "type": "string",
                    "description": "PEM certificate request (RSA 2048+ or ECDSA)."
                  }
                }
              },
              "example": {
                "csr": "string"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The certificate (`cert_pem`, serial, fingerprint, not_after).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "serial": {
                      "type": "string"
                    },
                    "fingerprint": {
                      "type": "string"
                    },
                    "subject": {
                      "type": "string"
                    },
                    "cert_pem": {
                      "type": "string"
                    },
                    "not_before": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "not_after": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KORFKBwlkJXlsIs66xTZqz5I0b",
                  "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                  "serial": "5f1c2a9e8b7d40e3a6c19f2b84d07e15",
                  "fingerprint": "fd8747f4bca05336bbe57747d664100e9e6123cec386f8249e9bd0db0343a15c",
                  "subject": "CN=loja-maputo-api,OU=kulpay-account:3Kg6UFQm6mLoKocUxwifotF73Ob",
                  "cert_pem": "-----BEGIN CERTIFICATE-----\nMIIB\u2026\n-----END CERTIFICATE-----\n",
                  "not_before": "2026-10-11T11:15:00Z",
                  "not_after": "2027-11-10T11:20:00Z",
                  "created_at": "2026-10-11T11:20:00Z"
                }
              }
            }
          },
          "400": {
            "description": "A malformed or unsigned CSR.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "body must be JSON: unexpected EOF",
                  "error": {
                    "code": "invalid_request",
                    "message": "body must be JSON: unexpected EOF"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listClientCertificates",
        "summary": "The account's client certificates",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "serial": {
                            "type": "string"
                          },
                          "fingerprint": {
                            "type": "string"
                          },
                          "subject": {
                            "type": "string"
                          },
                          "cert_pem": {
                            "type": "string"
                          },
                          "not_before": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "not_after": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KORFKBwlkJXlsIs66xTZqz5I0b",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "serial": "5f1c2a9e8b7d40e3a6c19f2b84d07e15",
                      "fingerprint": "fd8747f4bca05336bbe57747d664100e9e6123cec386f8249e9bd0db0343a15c",
                      "subject": "CN=loja-maputo-api,OU=kulpay-account:3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "cert_pem": "-----BEGIN CERTIFICATE-----\nMIIB\u2026\n-----END CERTIFICATE-----\n",
                      "not_before": "2026-10-11T11:15:00Z",
                      "not_after": "2027-11-10T11:20:00Z",
                      "created_at": "2026-10-11T11:20:00Z"
                    },
                    {
                      "id": "3KIAXK7qFPxMLx7TG5Xqym3JfAD",
                      "account_id": "3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "serial": "2b90d4c17e6a4f8195e03c7a1d62f9b0",
                      "fingerprint": "a1d55fdaaf8d6edd3d2276e7606ce4f166fafde576b6d11fca3b99154514ee08",
                      "subject": "CN=loja-maputo-api,OU=kulpay-account:3Kg6UFQm6mLoKocUxwifotF73Ob",
                      "cert_pem": "-----BEGIN CERTIFICATE-----\nMIIB\u2026\n-----END CERTIFICATE-----\n",
                      "not_before": "2025-11-02T09:55:00Z",
                      "not_after": "2026-12-02T10:00:00Z",
                      "revoked_at": "2026-10-11T11:25:00Z",
                      "created_at": "2025-11-02T10:00:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/client-certificates/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "revokeClientCertificate",
        "summary": "Revoke a client certificate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          }
        }
      }
    },
    "/v1/client-ca": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "clientCA",
        "summary": "The region's client CA certificate",
        "responses": {
          "200": {
            "description": "PEM.",
            "content": {
              "application/x-pem-file": {
                "schema": {
                  "type": "string"
                },
                "example": "-----BEGIN CERTIFICATE-----\nMIIBszCCAVmgAwIBAgIU\u2026\n-----END CERTIFICATE-----\n"
              }
            }
          }
        }
      }
    },
    "/v1/mtls": {
      "put": {
        "tags": [
          "Admin"
        ],
        "operationId": "setMTLS",
        "summary": "Require mutual TLS",
        "description": "With `required: true` every API call of the account must present a valid, unrevoked certificate the region's CA issued to it (TLS, or the edge's `client_cert_header`); otherwise `401 client_certificate_required`. The call that turns it on must itself present one, so the account cannot lock itself out. Records `credential.mtls.required`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "required": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "required": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{required}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "required": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "required": true
                }
              }
            }
          },
          "409": {
            "description": "`client_certificate_required`: call with a valid certificate first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "client_certificate_required",
                  "detail": "call with a valid client certificate issued to this account before requiring one",
                  "error": {
                    "code": "client_certificate_required",
                    "message": "call with a valid client certificate issued to this account before requiring one"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listApiKeys",
        "summary": "The account's API keys",
        "description": "Each key with its status, use count, last use and, for a rolled key, its expiry and successor. Requires `keys:manage` (or `admin:read`).",
        "responses": {
          "200": {
            "description": "`{data}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "last4": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "legacy": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "use_count": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K9gdgFU8tYDvMvO1agskQxf9LQ",
                      "mode": "test",
                      "name": "Servidor da loja",
                      "scopes": [
                        "payments:read",
                        "payments:write",
                        "refunds:write"
                      ],
                      "last4": "x9Qa",
                      "type": "rk",
                      "legacy": false,
                      "created_at": "2026-05-14T12:00:00Z",
                      "last_used_at": "2026-10-11T08:59:31Z",
                      "use_count": 18342,
                      "status": "active"
                    },
                    {
                      "id": "3KZvLCjA3YxljbE5HyYXiMogmjE",
                      "mode": "test",
                      "name": "Relat\u00f3rios",
                      "scopes": [
                        "payments:read"
                      ],
                      "last4": "T4mk",
                      "type": "rk",
                      "legacy": false,
                      "created_at": "2026-03-01T09:30:00Z",
                      "revoked_at": "2026-09-30T17:12:05Z",
                      "use_count": 412,
                      "status": "revoked"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "issueApiKey",
        "summary": "Issue an API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "enum": [
                      "sk",
                      "rk",
                      "pk"
                    ]
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "name": "Electricidade de Mo\u00e7ambique",
                "type": "sk",
                "scopes": [
                  "payments:write"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, with its secret once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "enum": [
                        "sk",
                        "rk",
                        "pk"
                      ]
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "last4": {
                      "type": "string"
                    },
                    "legacy": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "expiring",
                        "expired",
                        "revoked"
                      ]
                    },
                    "use_count": {
                      "type": "integer"
                    },
                    "last_used_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rolled_to": {
                      "type": "string"
                    },
                    "secret": {
                      "type": "string",
                      "description": "The key, on issue and roll only."
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "name": "Electricidade de Mo\u00e7ambique",
                  "type": "sk",
                  "scopes": [
                    "payments:write"
                  ],
                  "last4": "string",
                  "legacy": true,
                  "status": "active",
                  "use_count": 12,
                  "last_used_at": "2026-10-10T09:30:00Z",
                  "expires_at": "2026-10-10T09:30:00Z",
                  "rolled_to": "string",
                  "secret": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/api-keys/{id}/usage": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "apiKeyUsage",
        "summary": "An API key's usage",
        "description": "Last use and address, the locations it was used from, and requests and errors per UTC day with the error rate (PSP-8.5). Alerts are recorded as events: `credential.used_from_new_location` (a country or ASN the edge reports, else a network, after the first), `credential.usage_spike` (a day above 5x the 7-day average) and, per address, `credential.auth_failures_spike` (over 50 failed authentications in 5 minutes). Requires `keys:manage` (or `admin:read`).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 90,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{api_key_id, last_used_at, last_ip, locations, days: [{day, requests, errors}], requests, error_rate}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "api_key_id": {
                      "type": "string"
                    },
                    "last_used_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "last_ip": {
                      "type": "string"
                    },
                    "locations": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "days": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "api_key_id": {
                            "type": "string"
                          },
                          "day": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "requests": {
                            "type": "integer"
                          },
                          "errors": {
                            "type": "integer"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "requests": {
                      "type": "integer"
                    },
                    "error_rate": {
                      "type": "number"
                    }
                  }
                },
                "example": {
                  "api_key_id": "3KWPnAWDuhrhmqrcmpHEGEYXz1o",
                  "last_used_at": "2026-10-10T22:15:14.588143929Z",
                  "last_ip": "203.0.113.10",
                  "locations": [
                    "country:MZ",
                    "country:ZA"
                  ],
                  "days": [
                    {
                      "api_key_id": "3KWPnAWDuhrhmqrcmpHEGEYXz1o",
                      "day": "2026-10-10",
                      "account_id": "3KWPn38EFF4MNpUfOrr1582kbPF",
                      "requests": 3,
                      "errors": 1,
                      "updated_at": "2026-10-10T22:15:14.569661184Z"
                    }
                  ],
                  "requests": 3,
                  "error_rate": 0.3333333333333333
                }
              }
            }
          }
        }
      }
    },
    "/v1/api-keys/{id}/roll": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "rollApiKey",
        "summary": "Roll an API key with an overlap window",
        "description": "Issues a new key with the same type and scopes and sets the old key's expiry to now + grace (default 24 h, at most 7 days); both work until then, so the new key can be deployed with no downtime (PSP-8.3). Records `credential.api_key.rotated`; `credential.api_key.expired` follows at the expiry. A key can be rolled once.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "grace": {
                    "type": "string",
                    "example": "24h"
                  }
                }
              },
              "example": {
                "grace": "24h"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "`{key, previous}`: the new key with its secret, once, and the old key with its expiry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "last4": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "legacy": {
                          "type": "boolean"
                        },
                        "secret": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "use_count": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    },
                    "previous": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "last4": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "legacy": {
                          "type": "boolean"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "last_used_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "use_count": {
                          "type": "integer"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "rolled_to": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "key": {
                    "id": "3KQe7Rk2VbN9sTzW4LmXpA8cYdF",
                    "mode": "test",
                    "name": "Servidor da loja",
                    "scopes": [
                      "payments:read",
                      "payments:write",
                      "refunds:write"
                    ],
                    "last4": "Lr7d",
                    "type": "rk",
                    "legacy": false,
                    "secret": "kp_test_rk_\u2026",
                    "created_at": "2026-10-11T09:00:00Z",
                    "use_count": 0,
                    "status": "active"
                  },
                  "previous": {
                    "id": "3K9gdgFU8tYDvMvO1agskQxf9LQ",
                    "mode": "test",
                    "name": "Servidor da loja",
                    "scopes": [
                      "payments:read",
                      "payments:write",
                      "refunds:write"
                    ],
                    "last4": "x9Qa",
                    "type": "rk",
                    "legacy": false,
                    "created_at": "2026-05-14T12:00:00Z",
                    "last_used_at": "2026-10-11T08:59:31Z",
                    "use_count": 18342,
                    "expires_at": "2026-10-12T09:00:00Z",
                    "rolled_to": "3KQe7Rk2VbN9sTzW4LmXpA8cYdF",
                    "status": "expiring"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A grace period under 1 minute or over 7 days.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "grace is a duration of at least 1m, such as 24h",
                  "error": {
                    "code": "invalid_request",
                    "message": "grace is a duration of at least 1m, such as 24h"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`already_rolled`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "already_rolled",
                  "detail": "this key was already rolled or revoked",
                  "error": {
                    "code": "already_rolled",
                    "message": "this key was already rolled or revoked"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/api-keys/{id}": {
      "patch": {
        "tags": [
          "Admin"
        ],
        "operationId": "restrictApiKey",
        "summary": "Restrict an API key",
        "description": "Sets the key's expiry, IP allow-list and amount ceilings (PSP-8.4). A call from another address is `403 ip_not_allowed` (`credential.blocked_ip`); a transaction above a ceiling is `422 limit_exceeded` naming the rule (`api_key_max_amount`, `api_key_daily_amount`, `api_key_currency`). Keys are read from the database on every request, so the change holds on every node from the next one. Live keys expire after 12 months unless set otherwise, with reminders 30, 7 and 1 days before. Records `credential.api_key.restricted`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expires_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "no_expiry": {
                    "type": "boolean"
                  },
                  "allowed_ips": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "IPv4 or IPv6 addresses or CIDRs; empty = anywhere."
                  },
                  "max_amount": {
                    "type": "string",
                    "description": "Per-transaction ceiling."
                  },
                  "daily_amount": {
                    "type": "string",
                    "description": "Ceiling on what the key creates per UTC day."
                  },
                  "currency": {
                    "type": "string",
                    "description": "The ceilings' currency; other currencies are refused."
                  }
                }
              },
              "example": {
                "expires_at": "2026-10-10T09:30:00Z",
                "no_expiry": true,
                "allowed_ips": [
                  "string"
                ],
                "max_amount": "1250.00",
                "daily_amount": "1250.00",
                "currency": "MZN"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "last4": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "legacy": {
                      "type": "boolean"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "last_used_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "use_count": {
                      "type": "integer"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "status": {
                      "type": "string"
                    },
                    "allowed_ips": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "daily_amount": {
                      "type": "string"
                    },
                    "limit_currency": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3K9gdgFU8tYDvMvO1agskQxf9LQ",
                  "mode": "test",
                  "name": "Servidor da loja",
                  "scopes": [
                    "payments:read",
                    "payments:write",
                    "refunds:write"
                  ],
                  "last4": "x9Qa",
                  "type": "rk",
                  "legacy": false,
                  "created_at": "2026-05-14T12:00:00Z",
                  "last_used_at": "2026-10-11T08:59:31Z",
                  "use_count": 18342,
                  "expires_at": "2027-05-14T00:00:00Z",
                  "status": "expiring",
                  "allowed_ips": [
                    "41.220.160.10",
                    "196.28.240.0/24"
                  ],
                  "max_amount": "50000.00",
                  "daily_amount": "500000.00",
                  "limit_currency": "MZN"
                }
              }
            }
          },
          "400": {
            "description": "A malformed address, amount or currency, or an expiry in the past.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "body must be JSON: invalid character '}' looking for beginning of value",
                  "error": {
                    "code": "invalid_request",
                    "message": "body must be JSON: invalid character '}' looking for beginning of value"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key",
        "description": "At once, including during a roll's grace period.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          }
        }
      }
    },
    "/v1/event-types": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listEventTypes",
        "summary": "Event types an endpoint can subscribe to",
        "description": "Every webhook event type, with when it is sent and whether it is final. Requires `events:read` or `webhooks:manage`.",
        "responses": {
          "200": {
            "description": "`{data: [{type, when, terminal}], total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "when": {
                            "type": "string"
                          },
                          "terminal": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "type": "payment.succeeded",
                      "when": "The payment settled and the money is captured.",
                      "terminal": true
                    },
                    {
                      "type": "payment.failed",
                      "when": "The payment was declined or could not be completed.",
                      "terminal": true
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "createWebhookEndpoint",
        "summary": "Subscribe an endpoint to events",
        "description": "Registers an endpoint for the key's mode, subscribed to event types: exact names, families (`payout.*`) or everything (omit, or `*`) (PSP-7.5). Deliveries are signed and follow the 24-hour schedule and dead-letter queue of all webhooks. The signing secret is returned once. Records `webhook_endpoint.subscribed`. Requires `webhooks:manage`. Subscribe to `partner.alert` (or receive everything) to get KulPay's operational alerts on this endpoint too, besides the email to your contacts (P-2): an API key or client certificate expiring, a key found in public, a statement not received.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "contacts": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  }
                }
              },
              "example": {
                "url": "https://merchant.example.co.mz/kulpay/return",
                "event_types": [
                  "payment.succeeded"
                ],
                "contacts": [
                  "ops@example.co.mz"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "secret": {
                      "type": "string",
                      "description": "The signing secret, on create only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://merchant.example.co.mz/kulpay/return",
                  "enabled": true,
                  "status": "active",
                  "disabled_reason": "paused",
                  "event_types": [
                    "payment.succeeded"
                  ],
                  "contacts": [
                    "string"
                  ],
                  "secret": "string",
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "An unknown event type or a bad address.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "\"payment.completed\" is not a webhook event type (GET /v1/event-types lists them)",
                  "error": {
                    "code": "invalid_request",
                    "message": "\"payment.completed\" is not a webhook event type (GET /v1/event-types lists them)"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listWebhookEndpoints",
        "summary": "The account's webhook endpoints",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "url": {
                            "type": "string"
                          },
                          "enabled": {
                            "type": "boolean"
                          },
                          "status": {
                            "type": "string"
                          },
                          "event_types": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "ed25519": {
                            "type": "boolean"
                          },
                          "contacts": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "schema_versions": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer",
                              "minimum": 1
                            },
                            "description": "Event types pinned to a schema version (F3-18).",
                            "example": {
                              "bill_invoice.paid": 1
                            }
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K9fVrOSGHIvfS0z8BcX9qUgBf3",
                      "mode": "live",
                      "url": "https://loja.example.co.mz/webhooks/kulpay",
                      "enabled": true,
                      "status": "active",
                      "event_types": [
                        "payment.succeeded",
                        "payment.failed",
                        "refund.succeeded"
                      ],
                      "ed25519": true,
                      "contacts": [
                        "dev@example.co.mz"
                      ],
                      "created_at": "2026-07-02T09:15:00Z"
                    },
                    {
                      "id": "3K4nekciqFMaDnnsmlT6qBJ8alZ",
                      "mode": "test",
                      "url": "https://staging.loja.example.co.mz/webhooks/kulpay",
                      "enabled": false,
                      "status": "paused",
                      "disabled_reason": "paused",
                      "event_types": [
                        "*"
                      ],
                      "ed25519": false,
                      "created_at": "2026-06-20T16:40:12Z",
                      "disabled_at": "2026-10-01T08:00:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "getWebhookEndpoint",
        "summary": "A webhook endpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "secret": {
                      "type": "string",
                      "description": "The signing secret, on create only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://loja.example.co.mz/webhooks/kulpay",
                  "enabled": true,
                  "status": "active",
                  "disabled_reason": "paused",
                  "event_types": [
                    "payment.succeeded",
                    "bill_payment.succeeded"
                  ],
                  "contacts": [
                    "ops@example.co.mz"
                  ],
                  "secret": "string",
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "description": "Not found.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "webhook endpoint or delivery not found",
                  "error": {
                    "code": "not_found",
                    "message": "webhook endpoint or delivery not found"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Events"
        ],
        "operationId": "subscribeWebhookEndpoint",
        "summary": "Change an endpoint's subscription",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "event_types": [
                  "payment.succeeded"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "secret": {
                      "type": "string",
                      "description": "The signing secret, on create only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://merchant.example.co.mz/kulpay/return",
                  "enabled": true,
                  "status": "active",
                  "disabled_reason": "paused",
                  "event_types": [
                    "payment.succeeded"
                  ],
                  "contacts": [
                    "string"
                  ],
                  "secret": "string",
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "An unknown event type.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "\"payment.completed\" is not a webhook event type (GET /v1/event-types lists them)",
                  "error": {
                    "code": "invalid_request",
                    "message": "\"payment.completed\" is not a webhook event type (GET /v1/event-types lists them)"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Events"
        ],
        "operationId": "deleteWebhookEndpoint",
        "summary": "Remove a webhook endpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/pause": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "pauseWebhookEndpoint",
        "summary": "Pause an endpoint",
        "description": "Nothing is sent while paused; resume with `/enable`, then replay what it missed from an event. Records `webhook_endpoint.paused`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "secret": {
                      "type": "string",
                      "description": "The signing secret, on create only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://merchant.example.co.mz/kulpay/return",
                  "enabled": true,
                  "status": "active",
                  "disabled_reason": "paused",
                  "event_types": [
                    "payment.succeeded"
                  ],
                  "contacts": [
                    "string"
                  ],
                  "secret": "string",
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/test": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "testWebhookEndpoint",
        "summary": "Send a test event",
        "description": "A signed `webhook_endpoint.tested` event to this endpoint only, through the normal delivery path.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "`{delivery_id, event_id, event_type}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery_id": {
                      "type": "string"
                    },
                    "event_id": {
                      "type": "string"
                    },
                    "event_type": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "delivery_id": "3K7dZCinKGJgHK9WSjj79t6IWaX",
                  "event_id": "3K15E3LnuKxuG278PTGllbABGwy",
                  "event_type": "webhook_endpoint.tested"
                }
              }
            }
          },
          "409": {
            "description": "`endpoint_disabled`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "endpoint_disabled",
                  "detail": "the endpoint is disabled: enable it before replaying",
                  "error": {
                    "code": "endpoint_disabled",
                    "message": "the endpoint is disabled: enable it before replaying"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/deliveries": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "webhookEndpointDeliveries",
        "summary": "An endpoint's delivery history",
        "description": "The last 30 days, newest first: status, attempts, response code, latency, next attempt.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "event_id": {
                            "type": "string"
                          },
                          "event_type": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "response_code": {
                            "type": "integer"
                          },
                          "duration_ms": {
                            "type": "integer"
                          },
                          "last_error": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KwftWaywR8E0sQgebUy55IidZY",
                      "event_id": "3KP9ygtnmJTauCTmOray77nyB4C",
                      "event_type": "payment.failed",
                      "status": "pending",
                      "attempts": 2,
                      "response_code": 503,
                      "duration_ms": 10004,
                      "last_error": "HTTP 503",
                      "created_at": "2026-10-11T09:12:03Z",
                      "next_attempt_at": "2026-10-11T09:17:03Z"
                    },
                    {
                      "id": "3K7dZCinKGJgHK9WSjj79t6IWaX",
                      "event_id": "3K15E3LnuKxuG278PTGllbABGwy",
                      "event_type": "payment.succeeded",
                      "status": "delivered",
                      "attempts": 1,
                      "response_code": 200,
                      "duration_ms": 184,
                      "created_at": "2026-10-11T08:47:21Z",
                      "delivered_at": "2026-10-11T08:47:21Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/roll-secret": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "rollWebhookSecret",
        "summary": "Roll an endpoint's signing secret",
        "description": "A new secret (shown once); the old one keeps signing until `previous_secret_expires_at` (default 24 h, at most 7 days), so each delivery meanwhile carries a `v1=` per secret in `KulPay-Signature` (PSP-8.8). Records `credential.webhook_secret.rotated`. See the \"Rotate a webhook signing secret\" runbook.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "grace": {
                    "type": "string",
                    "example": "24h"
                  }
                }
              },
              "example": {
                "grace": "24h"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{endpoint (with secret), previous_secret_expires_at}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "mode": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string"
                        },
                        "enabled": {
                          "type": "boolean"
                        },
                        "status": {
                          "type": "string"
                        },
                        "event_types": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "ed25519": {
                          "type": "boolean"
                        },
                        "previous_secret_expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "contacts": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "secret": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "schema_versions": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer",
                            "minimum": 1
                          },
                          "description": "Event types pinned to a schema version (F3-18).",
                          "example": {
                            "bill_invoice.paid": 1
                          }
                        }
                      }
                    },
                    "previous_secret_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "endpoint": {
                    "id": "3K4lgJuN0uZKZkh8qikNmN22sbx",
                    "mode": "live",
                    "url": "https://loja.example.co.mz/webhooks/kulpay",
                    "enabled": true,
                    "status": "active",
                    "event_types": [
                      "payment.succeeded",
                      "payment.failed"
                    ],
                    "ed25519": false,
                    "previous_secret_expires_at": "2026-10-12T10:30:00Z",
                    "contacts": [
                      "dev@example.co.mz"
                    ],
                    "secret": "whsec_\u2026",
                    "created_at": "2026-05-04T14:22:10Z"
                  },
                  "previous_secret_expires_at": "2026-10-12T10:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "A grace under 1 minute or over 7 days.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the grace period is between 1 minute and 7 days",
                  "error": {
                    "code": "invalid_request",
                    "message": "the grace period is between 1 minute and 7 days"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/signing": {
      "put": {
        "tags": [
          "Events"
        ],
        "operationId": "setWebhookSigning",
        "summary": "Ed25519 signatures",
        "description": "With `ed25519: true` deliveries also carry `v1e=<base64 Ed25519 signature of \"<t>.<body>\">`, verifiable with the region's key from `/.well-known/kulpay-webhook-keys`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ed25519": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "ed25519": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string"
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "ed25519": {
                      "type": "boolean"
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "schema_versions": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "description": "Event types pinned to a schema version (F3-18).",
                      "example": {
                        "bill_invoice.paid": 1
                      }
                    }
                  }
                },
                "example": {
                  "id": "3K4lgJuN0uZKZkh8qikNmN22sbx",
                  "mode": "live",
                  "url": "https://loja.example.co.mz/webhooks/kulpay",
                  "enabled": true,
                  "status": "active",
                  "event_types": [
                    "payment.succeeded",
                    "payment.failed"
                  ],
                  "ed25519": true,
                  "contacts": [
                    "dev@example.co.mz"
                  ],
                  "created_at": "2026-05-04T14:22:10Z"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/kulpay-webhook-keys": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "webhookPublicKeys",
        "summary": "Webhook verification keys",
        "security": [],
        "description": "The region's Ed25519 public key for v1e= signatures (PSP-8.8). Public.",
        "responses": {
          "200": {
            "description": "`{keys: [{kid, alg: \"Ed25519\", region, public_key (base64)}]}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kid": {
                            "type": "string"
                          },
                          "alg": {
                            "type": "string"
                          },
                          "region": {
                            "type": "string"
                          },
                          "public_key": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "keys": [
                    {
                      "kid": "default-f6330aaf6000",
                      "alg": "Ed25519",
                      "region": "default",
                      "public_key": "YX1rYJ/oAMOwV+j+Q6NAaTmVHL62qYkDsXJ2ajMwWgQ="
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/replay": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "replayWebhookEndpoint",
        "summary": "Replay an endpoint's deliveries",
        "description": "Webhooks are tried immediately, then 1 min, 5 min, 15 min, 1 h, 3 h, 8 h and 24 h after the first attempt (10 s timeout each; any 2xx is success). The last failure dead-letters the delivery and alerts the endpoint's contacts; a `410` disables the endpoint, as do three days running with dead-lettered deliveries (PSP-9.5). This queues again every event from `since` on (default the last 30 days), or the dead-lettered ones (`status=exhausted`). Records `delivery.replayed`. Requires `webhooks:replay`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "An event id: replay it and everything after."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "enum": [
                "exhausted",
                "delivered"
              ]
            }
          }
        ],
        "responses": {
          "202": {
            "description": "`{\"queued\": n}`: fresh deliveries on the same schedule, each event once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "queued": 14
                }
              }
            }
          },
          "404": {
            "description": "Not this account's endpoint.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "webhook endpoint or delivery not found",
                  "error": {
                    "code": "not_found",
                    "message": "webhook endpoint or delivery not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`endpoint_disabled`: enable it first.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "endpoint_disabled",
                  "detail": "the endpoint is disabled: enable it before replaying",
                  "error": {
                    "code": "endpoint_disabled",
                    "message": "the endpoint is disabled: enable it before replaying"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/enable": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "enableWebhookEndpoint",
        "summary": "Enable a disabled endpoint",
        "description": "Clears the disable reason (`gone`, `dead_letters`); records `webhook_endpoint.enabled`. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint, enabled: the same representation as `GET /v1/webhook-endpoints/{id}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "secret": {
                      "type": "string",
                      "description": "The signing secret, on create only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://loja.example.co.mz/webhooks/kulpay",
                  "enabled": true,
                  "status": "active",
                  "event_types": [
                    "payment.succeeded",
                    "bill_payment.succeeded"
                  ],
                  "contacts": [
                    "ops@example.co.mz"
                  ],
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "description": "Not this account's endpoint.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "webhook endpoint or delivery not found",
                  "error": {
                    "code": "not_found",
                    "message": "webhook endpoint or delivery not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/contacts": {
      "put": {
        "tags": [
          "Events"
        ],
        "operationId": "setWebhookEndpointContacts",
        "summary": "Who is alerted about an endpoint",
        "description": "Emails told when a delivery is dead-lettered or the endpoint disabled; without any, a PSP's technical contact. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  }
                }
              },
              "example": {
                "emails": [
                  "ops@example.co.mz"
                ]
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Saved."
          },
          "400": {
            "description": "Not an email address.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "\"suporte.example.co.mz\" is not an email address",
                  "error": {
                    "code": "invalid_request",
                    "message": "\"suporte.example.co.mz\" is not an email address"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-deliveries/{id}/replay": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "replayWebhookDelivery",
        "summary": "Replay one delivery",
        "description": "Queues the delivery again with its frozen signed body. Requires `webhooks:replay`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "`{\"queued\": n}`: fresh deliveries on the same schedule, each event once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "queued": 1
                }
              }
            }
          },
          "404": {
            "description": "Not this account's delivery.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "webhook endpoint or delivery not found",
                  "error": {
                    "code": "not_found",
                    "message": "webhook endpoint or delivery not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`endpoint_disabled`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "endpoint_disabled",
                  "detail": "the endpoint is disabled: enable it before replaying",
                  "error": {
                    "code": "endpoint_disabled",
                    "message": "the endpoint is disabled: enable it before replaying"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/callbacks": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listCallbacks",
        "summary": "Provider callbacks: dead-letter queue and rejected",
        "description": "Callbacks are deduplicated by the provider's own event id within the PSP (else its reference and status, a body hash only as a last resort); a resend is answered 200 and recorded as `callback.duplicate`. Processing retries with exponential backoff (30 s doubling to 1 h, with jitter, 8 attempts); a callback that exhausts them moves to the dead-letter queue (`callback.dead_lettered`). `rejected` are those that failed their binding's verification. Bodies and headers are never returned. Requires `admin:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "enum": [
                "dead_lettered",
                "rejected",
                "failed"
              ],
              "default": "dead_lettered"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "provider": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "connector_id": {
                            "type": "string"
                          },
                          "transaction_id": {
                            "type": "string"
                          },
                          "remote_ip": {
                            "type": "string"
                          },
                          "status": {
                            "enum": [
                              "dead_lettered",
                              "rejected",
                              "failed",
                              "pending"
                            ]
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "error": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string"
                          },
                          "updated_at": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [],
                  "total": 0
                }
              }
            }
          },
          "400": {
            "description": "Another status.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "status must be dead_lettered, rejected or failed",
                  "error": {
                    "code": "invalid_request",
                    "message": "status must be dead_lettered, rejected or failed"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/callbacks/{id}/replay": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "replayCallback",
        "summary": "Replay a dead-lettered callback",
        "description": "Puts it back on the queue with fresh attempts, once the cause is fixed. A rejected callback cannot be replayed. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The callback, pending.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "provider": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "connector_id": {
                      "type": "string"
                    },
                    "transaction_id": {
                      "type": "string"
                    },
                    "remote_ip": {
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "dead_lettered",
                        "rejected",
                        "failed",
                        "pending"
                      ]
                    },
                    "attempts": {
                      "type": "integer"
                    },
                    "error": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider": "mpesa",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "connector_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "transaction_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "remote_ip": "string",
                  "status": "dead_lettered",
                  "attempts": 1,
                  "error": "string",
                  "created_at": "2026-10-10T09:30:00Z",
                  "updated_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such callback.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such callback",
                  "error": {
                    "code": "not_found",
                    "message": "no such callback"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`not_replayable`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_replayable",
                  "detail": "only a dead-lettered callback can be replayed (this one is rejected)",
                  "error": {
                    "code": "not_replayable",
                    "message": "only a dead-lettered callback can be replayed (this one is rejected)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/credentials": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listCredentials",
        "summary": "The PSP credential register",
        "description": "Every credential KulPay uses to call a PSP (PSP-8.9) with its owner, dates and rotation state; connector bindings appear as `connector_config`. Values never leave the vault. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "One account; default every account."
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "A register entry: metadata only, never the value.",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "connector_id": {
                            "type": "string"
                          },
                          "kind": {
                            "enum": [
                              "api_key",
                              "password",
                              "certificate",
                              "pgp_key",
                              "ssh_key",
                              "connector_config"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "owner": {
                            "type": "string"
                          },
                          "fingerprint": {
                            "type": "string",
                            "description": "A certificate's SHA-256 fingerprint."
                          },
                          "version": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "rotate_by": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "rotated_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "state": {
                            "enum": [
                              "ok",
                              "rotation_due",
                              "expiring",
                              "expired"
                            ]
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [],
                  "total": 0
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "createCredential",
        "summary": "Store a PSP credential",
        "description": "Seals the value under the active key-encryption key and records `credential.psp_secret.created`. It is due for rotation after `encryption.credential_rotation_days` (90). Requires `connectors:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind",
                  "name",
                  "value"
                ],
                "properties": {
                  "account_id": {
                    "type": "string",
                    "description": "Default: the caller's account."
                  },
                  "kind": {
                    "enum": [
                      "api_key",
                      "password",
                      "certificate",
                      "pgp_key",
                      "ssh_key"
                    ]
                  },
                  "name": {
                    "type": "string"
                  },
                  "owner": {
                    "type": "string"
                  },
                  "value": {
                    "type": "string",
                    "writeOnly": true,
                    "description": "Stored envelope-encrypted; never returned. A certificate is PEM."
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "A certificate's own expiry is used when omitted."
                  }
                }
              },
              "example": {
                "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "kind": "api_key",
                "name": "Electricidade de Mo\u00e7ambique",
                "owner": "string",
                "value": "1250.00",
                "expires_at": "2026-10-10T09:30:00Z"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The register entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A register entry: metadata only, never the value.",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "connector_id": {
                      "type": "string"
                    },
                    "kind": {
                      "enum": [
                        "api_key",
                        "password",
                        "certificate",
                        "pgp_key",
                        "ssh_key",
                        "connector_config"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "owner": {
                      "type": "string"
                    },
                    "fingerprint": {
                      "type": "string",
                      "description": "A certificate's SHA-256 fingerprint."
                    },
                    "version": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rotate_by": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rotated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "state": {
                      "enum": [
                        "ok",
                        "rotation_due",
                        "expiring",
                        "expired"
                      ]
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "connector_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "kind": "api_key",
                  "name": "Electricidade de Mo\u00e7ambique",
                  "owner": "string",
                  "fingerprint": "string",
                  "version": 1,
                  "created_at": "2026-10-10T09:30:00Z",
                  "expires_at": "2026-10-10T09:30:00Z",
                  "rotate_by": "2026-10-10T09:30:00Z",
                  "rotated_at": "2026-10-10T09:30:00Z",
                  "state": "ok"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a kind (api_key, password, certificate, pgp_key, ssh_key) and a name are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a kind (api_key, password, certificate, pgp_key, ssh_key) and a name are required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/credentials/{id}/rotate": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "rotateCredential",
        "summary": "Rotate a PSP credential",
        "description": "Replaces the value; the version moves on, the reminder restarts, and `credential.psp_secret.rotated` is recorded. A connector binding is rotated by configuring the connector again. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "properties": {
                  "account_id": {
                    "type": "string",
                    "description": "Default: the caller's account."
                  },
                  "kind": {
                    "enum": [
                      "api_key",
                      "password",
                      "certificate",
                      "pgp_key",
                      "ssh_key"
                    ]
                  },
                  "name": {
                    "type": "string"
                  },
                  "owner": {
                    "type": "string"
                  },
                  "value": {
                    "type": "string",
                    "writeOnly": true,
                    "description": "Stored envelope-encrypted; never returned. A certificate is PEM."
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "A certificate's own expiry is used when omitted."
                  }
                }
              },
              "example": {
                "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "kind": "api_key",
                "name": "Electricidade de Mo\u00e7ambique",
                "owner": "string",
                "value": "1250.00",
                "expires_at": "2026-10-10T09:30:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The register entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "A register entry: metadata only, never the value.",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "connector_id": {
                      "type": "string"
                    },
                    "kind": {
                      "enum": [
                        "api_key",
                        "password",
                        "certificate",
                        "pgp_key",
                        "ssh_key",
                        "connector_config"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "owner": {
                      "type": "string"
                    },
                    "fingerprint": {
                      "type": "string",
                      "description": "A certificate's SHA-256 fingerprint."
                    },
                    "version": {
                      "type": "integer"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rotate_by": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "rotated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "state": {
                      "enum": [
                        "ok",
                        "rotation_due",
                        "expiring",
                        "expired"
                      ]
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "account_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "connector_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "kind": "api_key",
                  "name": "Electricidade de Mo\u00e7ambique",
                  "owner": "string",
                  "fingerprint": "string",
                  "version": 1,
                  "created_at": "2026-10-10T09:30:00Z",
                  "expires_at": "2026-10-10T09:30:00Z",
                  "rotate_by": "2026-10-10T09:30:00Z",
                  "rotated_at": "2026-10-10T09:30:00Z",
                  "state": "ok"
                }
              }
            }
          },
          "404": {
            "description": "No such credential.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "credential or account not found",
                  "error": {
                    "code": "not_found",
                    "message": "credential or account not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/keys": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "operationId": "listKeys",
        "summary": "The account's API keys",
        "description": "Keys with their type (sk, rk, pk), scopes, last four characters and whether they are legacy (opaque keys issued before the kp_ format: rotate them). Requires admin:read or keys:manage.",
        "responses": {
          "200": {
            "description": "{data: [...]}",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "last4": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "legacy": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "use_count": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3K9gdgFU8tYDvMvO1agskQxf9LQ",
                      "mode": "test",
                      "name": "Servidor da loja",
                      "scopes": [
                        "payments:read",
                        "payments:write",
                        "refunds:write"
                      ],
                      "last4": "x9Qa",
                      "type": "rk",
                      "legacy": false,
                      "created_at": "2026-05-14T12:00:00Z",
                      "last_used_at": "2026-10-11T08:59:31Z",
                      "use_count": 18342,
                      "status": "active"
                    },
                    {
                      "id": "3KZvLCjA3YxljbE5HyYXiMogmjE",
                      "mode": "test",
                      "name": "Relat\u00f3rios",
                      "scopes": [
                        "payments:read"
                      ],
                      "last4": "T4mk",
                      "type": "rk",
                      "legacy": false,
                      "created_at": "2026-03-01T09:30:00Z",
                      "revoked_at": "2026-09-30T17:12:05Z",
                      "use_count": 412,
                      "status": "revoked"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Authentication"
        ],
        "operationId": "issueKey",
        "summary": "Issue an API key",
        "description": "Keys look like kp_<live|test>_<sk|rk|pk>_<random><checksum> (PSP-8.1) and are shown once. type: sk gets every non-platform scope, rk the listed scopes, pk only checkout:write and tokens:write (PSP-8.2); without a type, listed scopes make an rk, none an sk. Requires admin:write or keys:manage.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "sk",
                      "rk",
                      "pk"
                    ]
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "name": "recon",
                "type": "rk",
                "scopes": [
                  "recon:read"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The key, with its secret this once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "last4": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "legacy": {
                      "type": "boolean"
                    },
                    "secret": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "use_count": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3KWPqthUCgdYZBgaLTGUae2xfas",
                  "mode": "test",
                  "name": "recon",
                  "scopes": [
                    "recon:read"
                  ],
                  "last4": "71r8",
                  "type": "rk",
                  "legacy": false,
                  "secret": "kp_test_\u2026",
                  "created_at": "2026-10-10T22:15:44.211820237Z",
                  "use_count": 0,
                  "status": "active"
                }
              }
            }
          },
          "400": {
            "description": "Unknown scopes or type.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request body",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "The request body could not be parsed as valid JSON.",
                  "error": {
                    "code": "invalid_request",
                    "message": "The request body could not be parsed as valid JSON."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/keys/{id}": {
      "delete": {
        "tags": [
          "Authentication"
        ],
        "operationId": "revokeKey",
        "summary": "Revoke an API key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "404": {
            "description": "No such key for this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "The requested resource was not found.",
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/scopes": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "operationId": "listScopes",
        "summary": "The scope catalogue",
        "description": "Every scope, what it allows, and the scopes of each key type (PSP-8.2). A restricted key (`rk`) lists its scopes; a secret key (`sk`) gets every non-platform scope; a publishable key (`pk`) only `checkout:write` and `tokens:write`. Aliases pass the gates they stand for: `refunds:write` passes `payments:refund`, `recon:read` and `recon:resolve` the reconciliation scopes. A call without its scope is `403 insufficient_scope` with `missing_scope`. Any key may read this.",
        "responses": {
          "200": {
            "description": "`{data: [{name, description, platform, grants}], key_types}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "key_types": {
                      "type": "object",
                      "properties": {
                        "pk": {
                          "type": "object",
                          "properties": {
                            "description": {
                              "type": "string"
                            },
                            "scopes": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "rk": {
                          "type": "object",
                          "properties": {
                            "description": {
                              "type": "string"
                            }
                          }
                        },
                        "sk": {
                          "type": "object",
                          "properties": {
                            "description": {
                              "type": "string"
                            },
                            "scopes": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "name": "payments:read",
                      "description": "Read payments and their receipts."
                    },
                    {
                      "name": "payments:write",
                      "description": "Create payments and cancel them."
                    }
                  ],
                  "key_types": {
                    "pk": {
                      "description": "Publishable: tokenisation and checkout only.",
                      "scopes": [
                        "checkout:write",
                        "tokens:write"
                      ]
                    },
                    "rk": {
                      "description": "Restricted: the scopes listed when it is issued."
                    },
                    "sk": {
                      "description": "Secret: the account's full set (every non-platform scope).",
                      "scopes": [
                        "payments:read",
                        "payments:write"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/name-lookups": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "createNameLookup",
        "summary": "Look up a beneficiary name",
        "description": "Asks who holds a wallet (MSISDN) or account before you pay it (PSP-6.9): M-Pesa customer name query, eMola beneficiary name. With `method: \"cash_deposit\"` and a `reference`, a partner bank or agent asks whom a cash-deposit reference belongs to before taking the cash (PSP-7.7): the KulPay CMS answers through the `kulpay_cms` connector, and the response adds the deposit `constraints` and an `expires_at` (at most 15 minutes) until which the `lookup_id` can initiate a deposit (`POST /v1/cash-deposits/intents`). Routed like any other transaction type (NAME_LOOKUP). The name is returned to you and cached for at most 10 minutes (`cached: true` on a repeat); KulPay never stores it, and its event (name_lookup.completed) keeps only a masked form. Limited to 60 lookups per minute per key (429 with Retry-After). Requires `payouts:write` or `transactions:write`. A deposit reference is routed to its resolver by the reference routing rules (IO-1.1), KulOffice by default; the answer carries the resolver's destination, charges and settlement mode (IO-1.3). A reference may be resolved by KulPay itself among a group of billers (IO-1.4, IO-1.5): their presented invoices, then their products' lookups, 3 s each; when more than one finds it the answer is `ambiguous` with `options` (KulOffice may answer so too), and the requester chooses one (POST /v1/name-lookups/{lookup_id}/select) before initiating the deposit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "method"
                ],
                "properties": {
                  "method": {
                    "type": "string",
                    "example": "mpesa"
                  },
                  "msisdn": {
                    "type": "string",
                    "example": "258841234567"
                  },
                  "account": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string",
                    "example": "INV-1001",
                    "description": "A cash-deposit reference, with `method: \"cash_deposit\"` (PSP-7.7)."
                  },
                  "context": {
                    "type": "string",
                    "enum": [
                      "wallet",
                      "meter",
                      "invoice"
                    ],
                    "description": "Narrows a deposit reference to a kind when the requester knows it (IO-1.6), so the resolver does not offer a choice."
                  },
                  "biller": {
                    "type": "string",
                    "example": "EDM",
                    "description": "With a reference: asks that biller only (IO-1.6), KulPay resolving it (its presented invoices, then its products' lookups); for channels with no screen to choose an option on."
                  }
                }
              },
              "example": {
                "method": "mpesa",
                "msisdn": "258841234567",
                "account": "2003014567",
                "reference": "INV-1001"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The answer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lookup_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "found",
                        "not_found",
                        "ambiguous",
                        "unavailable"
                      ],
                      "description": "unavailable: the account exists but cannot receive (suspended or closed); show the depositor that the account is unavailable and that its holder should contact the provider. not_found: nobody has this reference."
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "credit_account_invalid",
                        "account_unavailable"
                      ],
                      "description": "Why a reference the resolver found is not_found: credit_account_invalid, the resolver named a credit account that is not one of the biller's registered active accounts (IO-4.1). A security exception is raised for KulPay's operators."
                    },
                    "name": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "cached": {
                      "type": "boolean"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "constraints": {
                      "type": "object",
                      "description": "What a deposit against the reference must respect.",
                      "properties": {
                        "currency": {
                          "type": "string"
                        },
                        "min_amount": {
                          "type": "string"
                        },
                        "max_amount": {
                          "type": "string"
                        },
                        "exact_amount": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Until when the lookup_id can initiate a deposit."
                    },
                    "destination": {
                      "type": "object",
                      "description": "Where a deposit against the reference goes, as the resolver decided (IO-1.3).",
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "wallet",
                            "kulpay_invoice",
                            "kulpay_bill"
                          ]
                        },
                        "customer_ref": {
                          "type": "string"
                        },
                        "invoice_id": {
                          "type": "string"
                        },
                        "biller": {
                          "type": "string"
                        },
                        "product": {
                          "type": "string"
                        },
                        "customer_reference": {
                          "type": "string"
                        },
                        "credit_account_id": {
                          "type": "string",
                          "description": "The registered account the value is credited to."
                        }
                      }
                    },
                    "charges": {
                      "type": "object",
                      "description": "The charge on the deposit, as the resolver set it: who pays it, the total and each participant's share. With payer customer, the depositor pays the amount plus the total.",
                      "properties": {
                        "payer": {
                          "type": "string",
                          "enum": [
                            "customer",
                            "kuloffice",
                            "biller"
                          ]
                        },
                        "total": {
                          "type": "string"
                        },
                        "split": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "role": {
                                "type": "string",
                                "enum": [
                                  "initiating_psp",
                                  "from_psp",
                                  "to_psp",
                                  "resolver",
                                  "kulpay"
                                ]
                              },
                              "amount": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    "settlement_mode": {
                      "type": "string",
                      "enum": [
                        "immediate",
                        "batch"
                      ],
                      "description": "How the destination is settled; absent: batch."
                    },
                    "options": {
                      "type": "array",
                      "description": "An ambiguous reference (IO-1.6): the destinations it may be. Choose one with POST /v1/name-lookups/{lookup_id}/select before initiating the deposit.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "option_id": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "masked_name": {
                            "type": "string"
                          },
                          "constraints": {
                            "type": "object"
                          }
                        }
                      }
                    },
                    "option_id": {
                      "type": "string",
                      "description": "The option chosen, once an ambiguous lookup is found."
                    }
                  }
                },
                "example": {
                  "lookup_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "status": "found",
                  "name": "Electricidade de Mo\u00e7ambique",
                  "method": "string",
                  "psp_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "cached": true,
                  "reference": "2003014567",
                  "constraints": {
                    "currency": "MZN",
                    "min_amount": "1250.00",
                    "max_amount": "1250.00",
                    "exact_amount": "1250.00",
                    "expires_at": "2026-10-10T09:30:00Z"
                  },
                  "expires_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "No method, or no MSISDN, account or reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a method and an MSISDN (or account) are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a method and an MSISDN (or account) are required"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`not_supported`: the method cannot look names up.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not supported",
                  "status": 422,
                  "code": "not_supported",
                  "detail": "name lookup is not supported for method card",
                  "rule": "method",
                  "error": {
                    "code": "not_supported",
                    "message": "name lookup is not supported for method card",
                    "rule": "method"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many lookups.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Too many lookups",
                  "status": 429,
                  "code": "rate_limited",
                  "detail": "Name lookups are limited to 60 per minute per key.",
                  "error": {
                    "code": "rate_limited",
                    "message": "Name lookups are limited to 60 per minute per key."
                  }
                }
              }
            }
          },
          "502": {
            "description": "The provider answered something KulPay cannot use (an incoherent resolver answer).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Provider error",
                  "status": 502,
                  "code": "upstream_error",
                  "detail": "The provider could not answer the lookup. Try again later.",
                  "error": {
                    "code": "upstream_error",
                    "message": "The provider could not answer the lookup. Try again later."
                  }
                }
              }
            }
          },
          "503": {
            "description": "The provider that answers the lookup did not answer (a timeout, a 5xx), or failed repeatedly and is not called until its circuit closes (IO-1.7). `Retry-After` says when to try again; a deposit the bank accepts meanwhile without a resolved lookup goes to suspense.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Provider unavailable",
                  "status": 503,
                  "code": "upstream_error",
                  "detail": "The provider that answers this lookup is not available. Try again after Retry-After seconds.",
                  "error": {
                    "code": "upstream_error",
                    "message": "The provider that answers this lookup is not available. Try again after Retry-After seconds."
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before trying again.",
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    "/v1/name-lookups/{lookup_id}/select": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "selectNameLookupOption",
        "summary": "Choose an option of an ambiguous reference",
        "description": "The option the requester chose (IO-1.6): the lookup becomes `found` with that option's destination, constraints and credit account, and a deposit can start from it (an intent refuses an ambiguous lookup). Choosing the same option again returns the same lookup; another one before any deposit started replaces it; after, `409 option_locked`. Records `name_lookup.option_selected`. Requires `payouts:write` or `transactions:write`.",
        "parameters": [
          {
            "name": "lookup_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "option_id"
                ],
                "properties": {
                  "option_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "option_id": "EDM:CREDELEC"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lookup, found with the option chosen.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "lookup_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "found",
                        "not_found",
                        "ambiguous",
                        "unavailable"
                      ],
                      "description": "unavailable: the account exists but cannot receive (suspended or closed); show the depositor that the account is unavailable and that its holder should contact the provider. not_found: nobody has this reference."
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "credit_account_invalid",
                        "account_unavailable"
                      ],
                      "description": "Why a reference the resolver found is not_found: credit_account_invalid, the resolver named a credit account that is not one of the biller's registered active accounts (IO-4.1). A security exception is raised for KulPay's operators."
                    },
                    "name": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "cached": {
                      "type": "boolean"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "constraints": {
                      "type": "object",
                      "description": "What a deposit against the reference must respect.",
                      "properties": {
                        "currency": {
                          "type": "string"
                        },
                        "min_amount": {
                          "type": "string"
                        },
                        "max_amount": {
                          "type": "string"
                        },
                        "exact_amount": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Until when the lookup_id can initiate a deposit."
                    },
                    "destination": {
                      "type": "object",
                      "description": "Where a deposit against the reference goes, as the resolver decided (IO-1.3).",
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "wallet",
                            "kulpay_invoice",
                            "kulpay_bill"
                          ]
                        },
                        "customer_ref": {
                          "type": "string"
                        },
                        "invoice_id": {
                          "type": "string"
                        },
                        "biller": {
                          "type": "string"
                        },
                        "product": {
                          "type": "string"
                        },
                        "customer_reference": {
                          "type": "string"
                        },
                        "credit_account_id": {
                          "type": "string",
                          "description": "The registered account the value is credited to."
                        }
                      }
                    },
                    "charges": {
                      "type": "object",
                      "description": "The charge on the deposit, as the resolver set it: who pays it, the total and each participant's share. With payer customer, the depositor pays the amount plus the total.",
                      "properties": {
                        "payer": {
                          "type": "string",
                          "enum": [
                            "customer",
                            "kuloffice",
                            "biller"
                          ]
                        },
                        "total": {
                          "type": "string"
                        },
                        "split": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "role": {
                                "type": "string",
                                "enum": [
                                  "initiating_psp",
                                  "from_psp",
                                  "to_psp",
                                  "resolver",
                                  "kulpay"
                                ]
                              },
                              "amount": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    "settlement_mode": {
                      "type": "string",
                      "enum": [
                        "immediate",
                        "batch"
                      ],
                      "description": "How the destination is settled; absent: batch."
                    },
                    "options": {
                      "type": "array",
                      "description": "An ambiguous reference (IO-1.6): the destinations it may be. Choose one with POST /v1/name-lookups/{lookup_id}/select before initiating the deposit.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "option_id": {
                            "type": "string"
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "masked_name": {
                            "type": "string"
                          },
                          "constraints": {
                            "type": "object"
                          }
                        }
                      }
                    },
                    "option_id": {
                      "type": "string",
                      "description": "The option chosen, once an ambiguous lookup is found."
                    }
                  }
                },
                "example": {
                  "lookup_id": "3KXg0Yd2bW1n0mQe8VtR5cJ7pLs",
                  "status": "found",
                  "method": "cash_deposit",
                  "cached": false,
                  "reference": "01234567890",
                  "expires_at": "2026-10-15T10:15:00Z",
                  "option_id": "AGUAS:FACTURA",
                  "destination": {
                    "kind": "kulpay_invoice",
                    "invoice_id": "3KXg0ZQ1yH8sT4aVn2Lk9Wd6rFb",
                    "credit_account_id": "biller:AGUAS"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No method, or no MSISDN, account or reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a method and an MSISDN (or account) are required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a method and an MSISDN (or account) are required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "`not_found`: no such lookup for this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 400,
                  "code": "not_found",
                  "detail": "a method and an MSISDN (or account) are required",
                  "error": {
                    "code": "not_found",
                    "message": "a method and an MSISDN (or account) are required"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`option_locked`: a deposit already started from the lookup.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Option locked",
                  "status": 409,
                  "code": "option_locked",
                  "detail": "payments: a deposit already started from this lookup: its option cannot change",
                  "error": {
                    "code": "option_locked",
                    "message": "payments: a deposit already started from this lookup: its option cannot change"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk": {
      "get": {
        "tags": [
          "Risk"
        ],
        "operationId": "getRisk",
        "summary": "Risk rules in force",
        "description": "Every transaction passes a risk stage before routing (PSP-6.3). The built-in engine applies these rules; the most severe action of the rules that fire decides: decline (refused before any provider call, 422 `risk_declined`; the caller is not told which rule), review (held pending for a compliance officer; it expires after `review_timeout_minutes`, default 1440), challenge (proceeds; the connector is asked to step up authentication via metadata `kulpay_challenge=required`), else approve. Each decision is recorded with the rule ids that fired (transaction.risk.approved, review_required, declined, challenged). Velocity counts phones, cards and accounts as hashes. Requires `admin:read`.",
        "responses": {
          "200": {
            "description": "Active rules, versions, pending approvals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "active_version": {
                      "type": "integer"
                    },
                    "active": {
                      "type": "object",
                      "properties": {
                        "rules": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              },
                              "action": {
                                "type": "string"
                              },
                              "min_amount": {
                                "type": "string"
                              }
                            }
                          }
                        }
                      }
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "activated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "active_version": 1,
                  "active": {
                    "rules": [
                      {
                        "id": "big",
                        "type": "amount",
                        "action": "review",
                        "min_amount": "1000"
                      }
                    ]
                  },
                  "versions": [
                    {
                      "id": "3KWPqnzI7EnerX06CYufMI2Dvlf",
                      "version": 1,
                      "status": "active",
                      "reason": "hold large payments",
                      "approval_id": "3KWPqlI8eTHLPeIyqzeOkflh7Tm",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:43.531498263Z",
                      "activated_at": "2026-10-10T22:15:43.556916651Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk/versions": {
      "post": {
        "tags": [
          "Risk"
        ],
        "operationId": "proposeRisk",
        "summary": "Prepare a risk rules version",
        "description": "Active once a second person approves it (`risk.activate`). A rule of type `limit` (F3-9) caps bill payments per `payer_phone` (E.164, counted across channels), `reference` or `channel`, over a `window` of `day` or `month` (Maputo time), by `max_count` and/or `max_amount` (one currency); its action is `decline` and a refusal answers 422 `limit_exceeded` with `rule`. `suspicion: true` on any rule opens a compliance case when it fires (F3-8).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rules",
                  "reason"
                ],
                "properties": {
                  "review_timeout_minutes": {
                    "type": "integer",
                    "maximum": 10080
                  },
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "id",
                        "type",
                        "action"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "velocity",
                            "amount",
                            "country",
                            "blocklist"
                          ]
                        },
                        "action": {
                          "type": "string",
                          "enum": [
                            "review",
                            "decline",
                            "challenge"
                          ]
                        },
                        "methods": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "key": {
                          "type": "string",
                          "enum": [
                            "phone",
                            "card",
                            "account"
                          ]
                        },
                        "window_seconds": {
                          "type": "integer"
                        },
                        "max_count": {
                          "type": "integer"
                        },
                        "max_amount": {
                          "type": "string"
                        },
                        "min_amount": {
                          "type": "string"
                        },
                        "currencies": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "countries": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "list": {
                          "type": "string",
                          "enum": [
                            "beneficiary",
                            "phone",
                            "card"
                          ]
                        }
                      }
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "rules": [
                  {
                    "id": "big",
                    "type": "amount",
                    "min_amount": "1000",
                    "action": "review"
                  }
                ],
                "reason": "hold large payments"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    },
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPqlI8eTHLPeIyqzeOkflh7Tm",
                    "kind": "risk.activate",
                    "subject_id": "3KWPqnzI7EnerX06CYufMI2Dvlf",
                    "tenant_id": "platform",
                    "reason": "risk rules v1: hold large payments",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:43.5330168Z"
                  },
                  "message": "risk rules version prepared; a second person must approve it",
                  "version": {
                    "id": "3KWPqnzI7EnerX06CYufMI2Dvlf",
                    "version": 1,
                    "status": "pending",
                    "reason": "hold large payments",
                    "approval_id": "3KWPqlI8eTHLPeIyqzeOkflh7Tm",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:43.531498263Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid rules, each problem listed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "rule 2: an id is required; velocity_phone: window_seconds is 1 to 31 days",
                  "error": {
                    "code": "invalid_request",
                    "message": "rule 2: an id is required; velocity_phone: window_seconds is 1 to 31 days"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk/reviews": {
      "get": {
        "tags": [
          "Risk"
        ],
        "operationId": "listRiskReviews",
        "summary": "Payments held for review",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "approved",
                "declined",
                "expired"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reviews, oldest first, with the rules that fired and the due time.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "rules": {
                            "type": "string"
                          },
                          "reasons": {
                            "type": "string"
                          },
                          "due_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPqk1L2HZGjVZNlguB7EJOnVo",
                      "payment_id": "3KWPqo4UmMf7pgnGgcnWvrWFihC",
                      "account_id": "3KWPqoZJjidjdKEJj56o2fPtD9Y",
                      "mode": "test",
                      "status": "open",
                      "rules": "[\"big\"]",
                      "reasons": "[\"amount 5000 MZN is at or above 1000\"]",
                      "due_at": "2026-10-11T22:15:43.61286086Z",
                      "created_at": "2026-10-10T22:15:43.61286086Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk/reviews/{id}/decide": {
      "post": {
        "tags": [
          "Risk"
        ],
        "operationId": "decideRiskReview",
        "summary": "Approve or decline a held payment",
        "description": "Approve: the payment is routed and submitted. Decline: it fails (`transaction_not_allowed`). Recorded as transaction.risk.review_decided.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "approve",
                  "reason"
                ],
                "properties": {
                  "approve": {
                    "type": "boolean"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "approve": true,
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{payment_id, status}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "payment_id": "3KUYN1JYs7zLC4MODFfSw8UqHA6",
                  "status": "failed"
                }
              }
            }
          },
          "404": {
            "description": "No such review.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such review",
                  "error": {
                    "code": "not_found",
                    "message": "no such review"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk/blocklist": {
      "get": {
        "tags": [
          "Risk"
        ],
        "operationId": "listRiskBlocks",
        "summary": "The risk block list",
        "description": "Entries are kept as hashes; only a label (the last digits) is shown.",
        "responses": {
          "200": {
            "description": "Entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "list": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPqlGMif6gHkmvCvziGSIU3Nu",
                      "list": "phone",
                      "label": "\u20264567",
                      "reason": "fraud report",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:43.755506637Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Risk"
        ],
        "operationId": "addRiskBlock",
        "summary": "Block a beneficiary, phone or card",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "list",
                  "value",
                  "reason"
                ],
                "properties": {
                  "list": {
                    "type": "string",
                    "enum": [
                      "beneficiary",
                      "phone",
                      "card"
                    ]
                  },
                  "value": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "list": "phone",
                "value": "+258 84 123 4567",
                "reason": "fraud report"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "list": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    },
                    "created_by": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KWPqlGMif6gHkmvCvziGSIU3Nu",
                  "list": "phone",
                  "label": "\u20264567",
                  "reason": "fraud report",
                  "created_by": "key_accounts",
                  "created_at": "2026-10-10T22:15:43.755506637Z"
                }
              }
            }
          },
          "400": {
            "description": "Missing fields or already listed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the value is already on the phone list",
                  "error": {
                    "code": "invalid_request",
                    "message": "the value is already on the phone list"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/risk/blocklist/{id}": {
      "delete": {
        "tags": [
          "Risk"
        ],
        "operationId": "removeRiskBlock",
        "summary": "Unblock an entry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reason",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No such entry.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such entry",
                  "error": {
                    "code": "not_found",
                    "message": "no such entry"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/routing": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "getRouting",
        "summary": "Routing rules in force",
        "description": "The active rule set and its version, every version, and pending approvals (PSP-6.4). A PSP is eligible when its status allows the merchant (live; pilot for its allow-listed merchants; any working status in test mode), its circuit is closed, its capability matrix allows the request and its daily and monthly limits have room. With no matching rule a payment runs on its account's own connector binding. Failover (`failover`, `max_failovers`, default 2): after a soft decline or an error before the request was sent (connection refused, DNS failure, open circuit) the payment moves to the next eligible target, never on an unknown outcome or after the PSP may have accepted it; each move is `transaction.failover.attempted` and each attempt its own submit stage. Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "The rule set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "active_version": {
                      "type": "integer"
                    },
                    "active": {
                      "type": "object",
                      "properties": {
                        "rules": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "match": {
                                "type": "object",
                                "properties": {
                                  "methods": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  }
                                }
                              },
                              "targets": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "psp_id": {
                                      "type": "string"
                                    },
                                    "weight": {
                                      "type": "integer"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_by": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "activated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "active_version": 1,
                  "active": {
                    "rules": [
                      {
                        "id": "cards",
                        "match": {
                          "methods": [
                            "card"
                          ]
                        },
                        "targets": [
                          {
                            "psp_id": "3KWPqtgPmeCaYplKVRxyJiriQOG",
                            "weight": 100
                          }
                        ]
                      }
                    ]
                  },
                  "versions": [
                    {
                      "id": "3KWPqrCkfWzVjEDju03YNzH5ntR",
                      "version": 1,
                      "status": "active",
                      "reason": "cards to Rota",
                      "approval_id": "3KWPqpGgl5DVi0nsBOTijTV7GWK",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:44.903403745Z",
                      "activated_at": "2026-10-10T22:15:44.945952516Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/routing/versions": {
      "post": {
        "tags": [
          "Routing"
        ],
        "operationId": "proposeRouting",
        "summary": "Prepare a routing rules version",
        "description": "A new version of the whole rule set; it is active once a second person approves it (`routing.activate`, POST /v1/admin/approvals/{id}/decide). Requires `connectors:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rules",
                  "reason"
                ],
                "properties": {
                  "rules": {
                    "type": "array",
                    "description": "Ordered rules: the first that matches decides.",
                    "items": {
                      "type": "object",
                      "required": [
                        "id",
                        "targets"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "match": {
                          "type": "object",
                          "description": "Empty fields match anything.",
                          "properties": {
                            "methods": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "types": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "SALE"
                              ]
                            },
                            "currencies": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "countries": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "min_amount": {
                              "type": "string"
                            },
                            "max_amount": {
                              "type": "string"
                            },
                            "merchants": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "Account ids or metadata.merchant_id values."
                            },
                            "bin_ranges": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "from": {
                                    "type": "string"
                                  },
                                  "to": {
                                    "type": "string"
                                  }
                                }
                              }
                            },
                            "phone_prefixes": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "25884"
                              ]
                            },
                            "reference": {
                              "type": "object",
                              "description": "The shape of a deposit reference (IO-1.1), on the reference upper-cased and without spaces: every condition set must hold. A lookup whose reference matches no rule goes to the default resolver.",
                              "properties": {
                                "prefixes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "suffixes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "min_length": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 128
                                },
                                "max_length": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 128
                                },
                                "digits_only": {
                                  "type": "boolean"
                                },
                                "pattern": {
                                  "type": "string",
                                  "maxLength": 256,
                                  "description": "A regular expression the whole reference must match."
                                }
                              }
                            }
                          }
                        },
                        "targets": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "psp_id",
                              "weight"
                            ],
                            "properties": {
                              "psp_id": {
                                "type": "string"
                              },
                              "weight": {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 100,
                                "description": "Share among the eligible targets; 0 is a fallback only."
                              }
                            }
                          }
                        },
                        "failover": {
                          "type": "boolean",
                          "default": true
                        },
                        "max_failovers": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 5,
                          "default": 2
                        }
                      }
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "rules": [
                  {
                    "id": "cards",
                    "match": {
                      "methods": [
                        "card"
                      ]
                    },
                    "targets": [
                      {
                        "psp_id": "3KWPqtgPmeCaYplKVRxyJiriQOG",
                        "weight": 100
                      }
                    ]
                  }
                ],
                "reason": "cards to Rota"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared; `approval` is the request to approve.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    },
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPqpGgl5DVi0nsBOTijTV7GWK",
                    "kind": "routing.activate",
                    "subject_id": "3KWPqrCkfWzVjEDju03YNzH5ntR",
                    "tenant_id": "platform",
                    "reason": "routing rules v1: cards to Rota",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:15:44.905300473Z"
                  },
                  "message": "routing version prepared; a second person must approve it",
                  "version": {
                    "id": "3KWPqrCkfWzVjEDju03YNzH5ntR",
                    "version": 1,
                    "status": "pending",
                    "reason": "cards to Rota",
                    "approval_id": "3KWPqpGgl5DVi0nsBOTijTV7GWK",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:44.903403745Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid rules (each problem listed) or one already pending.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "at least one rule is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "at least one rule is required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/routing/versions/{version}": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "getRoutingVersion",
        "summary": "One routing rules version",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The version and its rules.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "object",
                      "properties": {
                        "rules": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "match": {
                                "type": "object",
                                "properties": {
                                  "methods": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  }
                                }
                              },
                              "targets": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "psp_id": {
                                      "type": "string"
                                    },
                                    "weight": {
                                      "type": "integer"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "activated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "rules": {
                    "rules": [
                      {
                        "id": "cards",
                        "match": {
                          "methods": [
                            "card"
                          ]
                        },
                        "targets": [
                          {
                            "psp_id": "3KWPqtgPmeCaYplKVRxyJiriQOG",
                            "weight": 100
                          }
                        ]
                      }
                    ]
                  },
                  "version": {
                    "id": "3KWPqrCkfWzVjEDju03YNzH5ntR",
                    "version": 1,
                    "status": "active",
                    "reason": "cards to Rota",
                    "approval_id": "3KWPqpGgl5DVi0nsBOTijTV7GWK",
                    "created_by": "key_accounts",
                    "created_at": "2026-10-10T22:15:44.903403745Z",
                    "activated_at": "2026-10-10T22:15:44.945952516Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such version.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such routing version",
                  "error": {
                    "code": "not_found",
                    "message": "no such routing version"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/routing/versions/{version}/rollback": {
      "post": {
        "tags": [
          "Routing"
        ],
        "operationId": "rollbackRouting",
        "summary": "Roll back to a routing rules version",
        "description": "Prepares a copy of an earlier version, through the same approval.",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "based_on": {
                          "type": "integer"
                        },
                        "approval_id": {
                          "type": "string"
                        },
                        "created_by": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "3KymVIVtt0XsYONnValT0cGW9OX",
                    "version": 8,
                    "status": "pending",
                    "reason": "Repor as regras da vers\u00e3o 5 ap\u00f3s falhas no novo conector",
                    "based_on": 5,
                    "approval_id": "3Kh8Qx4H3yHpjhVVX645vFcRXuU",
                    "created_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "created_at": "2026-10-11T07:48:20Z"
                  },
                  "approval": {
                    "id": "3Kh8Qx4H3yHpjhVVX645vFcRXuU",
                    "kind": "routing.activate",
                    "subject_id": "3KymVIVtt0XsYONnValT0cGW9OX",
                    "tenant_id": "platform",
                    "reason": "routing rules v8: Repor as regras da vers\u00e3o 5 ap\u00f3s falhas no novo conector",
                    "prepared_by": "3KWTqydMTGykEIsWuGEP683gqgc",
                    "status": "pending",
                    "created_at": "2026-10-11T07:48:20Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such version.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such routing version",
                  "error": {
                    "code": "not_found",
                    "message": "no such routing version"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/routing/dry-run": {
      "post": {
        "tags": [
          "Routing"
        ],
        "operationId": "dryRunRouting",
        "summary": "Dry-run routing rules on recent traffic",
        "description": "Replays the last `days` (default 7) of a mode's payments through `rules` (or stored `version`) and returns the expected split per PSP (weighted targets count by their share), the current split, hits per rule, payments that fall back to their own binding and those no PSP could take. Card BIN and phone-prefix conditions are not replayed (not stored with payments). Nothing is reserved or changed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rules": {
                    "type": "array",
                    "description": "Ordered rules: the first that matches decides.",
                    "items": {
                      "type": "object",
                      "required": [
                        "id",
                        "targets"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "match": {
                          "type": "object",
                          "description": "Empty fields match anything.",
                          "properties": {
                            "methods": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "types": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "SALE"
                              ]
                            },
                            "currencies": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "countries": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "min_amount": {
                              "type": "string"
                            },
                            "max_amount": {
                              "type": "string"
                            },
                            "merchants": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "Account ids or metadata.merchant_id values."
                            },
                            "bin_ranges": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "from": {
                                    "type": "string"
                                  },
                                  "to": {
                                    "type": "string"
                                  }
                                }
                              }
                            },
                            "phone_prefixes": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "example": [
                                "25884"
                              ]
                            },
                            "reference": {
                              "type": "object",
                              "description": "The shape of a deposit reference (IO-1.1), on the reference upper-cased and without spaces: every condition set must hold. A lookup whose reference matches no rule goes to the default resolver.",
                              "properties": {
                                "prefixes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "suffixes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "min_length": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 128
                                },
                                "max_length": {
                                  "type": "integer",
                                  "minimum": 0,
                                  "maximum": 128
                                },
                                "digits_only": {
                                  "type": "boolean"
                                },
                                "pattern": {
                                  "type": "string",
                                  "maxLength": 256,
                                  "description": "A regular expression the whole reference must match."
                                }
                              }
                            }
                          }
                        },
                        "targets": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "psp_id",
                              "weight"
                            ],
                            "properties": {
                              "psp_id": {
                                "type": "string"
                              },
                              "weight": {
                                "type": "integer",
                                "minimum": 0,
                                "maximum": 100,
                                "description": "Share among the eligible targets; 0 is a fallback only."
                              }
                            }
                          }
                        },
                        "failover": {
                          "type": "boolean",
                          "default": true
                        },
                        "max_failovers": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 5,
                          "default": 2
                        }
                      }
                    }
                  },
                  "version": {
                    "type": "integer"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "test",
                      "live"
                    ]
                  },
                  "days": {
                    "type": "integer",
                    "maximum": 31
                  }
                }
              },
              "example": {
                "rules": [
                  {
                    "id": "cards",
                    "match": {
                      "methods": [
                        "card"
                      ]
                    },
                    "targets": [
                      {
                        "psp_id": "3KWPqtgPmeCaYplKVRxyJiriQOG",
                        "weight": 100
                      }
                    ]
                  }
                ],
                "mode": "test"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{sample, by_psp: [{psp_id, name, expected, share, current}], by_rule, fallback, not_routed, truncated, limitation}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "window": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "sample": {
                      "type": "integer"
                    },
                    "by_psp": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "psp_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "expected": {
                            "type": "integer"
                          },
                          "share": {
                            "type": "integer"
                          },
                          "current": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "by_rule": {
                      "type": "object",
                      "properties": {
                        "cards": {
                          "type": "integer"
                        }
                      }
                    },
                    "fallback": {
                      "type": "integer"
                    },
                    "not_routed": {
                      "type": "integer"
                    },
                    "truncated": {
                      "type": "boolean"
                    },
                    "limitation": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "window": "2026-10-03T22:15:44Z to 2026-10-10T22:15:44Z",
                  "sample": 3,
                  "by_psp": [
                    {
                      "psp_id": "3KWPqtgPmeCaYplKVRxyJiriQOG",
                      "name": "Rota Lda",
                      "expected": 3,
                      "share": 1,
                      "current": 0
                    }
                  ],
                  "by_rule": {
                    "cards": 3
                  },
                  "fallback": 0,
                  "not_routed": 0,
                  "truncated": false,
                  "limitation": "Card BIN and phone-prefix conditions are not replayed: payments do not store the card number or phone; such rules are counted as not matching."
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/code-mappings": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "listCodeMappings",
        "summary": "Provider code mappings",
        "description": "Providers with a table (built-in: mpesa, emola, iso8583; others normalise through KulPay's vocabulary), the codes seen without a mapping, and the families. Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "Mappings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "provider": {
                            "type": "string"
                          },
                          "active_version": {
                            "type": "integer"
                          },
                          "entries": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "unmapped": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UnmappedCode"
                      }
                    },
                    "families": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "active_version": 0,
                      "entries": 5,
                      "provider": "emola"
                    },
                    {
                      "active_version": 0,
                      "entries": 26,
                      "provider": "iso8583"
                    }
                  ],
                  "families": [
                    "approved",
                    "soft_decline"
                  ],
                  "unmapped": []
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/code-mappings/{provider}": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "getCodeMapping",
        "summary": "A provider's code table",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "mpesa"
            }
          }
        ],
        "description": "The effective table (built-in entries with the active version's on top), the versions and pending approvals.",
        "responses": {
          "200": {
            "description": "The table.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string"
                    },
                    "active_version": {
                      "type": "integer"
                    },
                    "entries": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/CodeEntry"
                      }
                    },
                    "overrides": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/CodeEntry"
                      }
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CodeMapping"
                      }
                    },
                    "pending_approvals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApprovalRequest"
                      }
                    }
                  }
                },
                "example": {
                  "provider": "mpesa",
                  "active_version": 0,
                  "entries": {
                    "INS-0": {
                      "code": "",
                      "family": "approved",
                      "description": "Request processed successfully"
                    },
                    "INS-1": {
                      "code": "processing_error",
                      "family": "error",
                      "description": "Internal error"
                    },
                    "INS-10": {
                      "code": "duplicate_transaction",
                      "family": "hard_decline",
                      "description": "Duplicate transaction"
                    },
                    "INS-13": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid shortcode used"
                    },
                    "INS-14": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid reference used"
                    },
                    "INS-15": {
                      "code": "amount_mismatch",
                      "family": "hard_decline",
                      "description": "Invalid amount used"
                    },
                    "INS-16": {
                      "code": "provider_unavailable",
                      "family": "error",
                      "description": "Unable to handle the request due to a temporary overload"
                    },
                    "INS-17": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid transaction reference length"
                    },
                    "INS-18": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid transaction ID"
                    },
                    "INS-19": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid third-party reference"
                    },
                    "INS-2": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid API key"
                    },
                    "INS-20": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Not all parameters provided"
                    },
                    "INS-2001": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Initiator authentication error"
                    },
                    "INS-2002": {
                      "code": "account_not_found",
                      "family": "hard_decline",
                      "description": "Receiver invalid"
                    },
                    "INS-2006": {
                      "code": "insufficient_funds",
                      "family": "soft_decline",
                      "description": "Insufficient balance"
                    },
                    "INS-2051": {
                      "code": "account_not_found",
                      "family": "hard_decline",
                      "description": "MSISDN invalid"
                    },
                    "INS-2057": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Language code invalid"
                    },
                    "INS-21": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Parameter validations failed"
                    },
                    "INS-22": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid operation type"
                    },
                    "INS-23": {
                      "code": "",
                      "family": "unknown",
                      "description": "Unknown status: query again"
                    },
                    "INS-24": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid initiator identifier"
                    },
                    "INS-25": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Invalid security credential"
                    },
                    "INS-26": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Not authorized"
                    },
                    "INS-4": {
                      "code": "transaction_not_allowed",
                      "family": "hard_decline",
                      "description": "User is not active"
                    },
                    "INS-5": {
                      "code": "user_cancelled",
                      "family": "hard_decline",
                      "description": "Transaction cancelled by customer"
                    },
                    "INS-6": {
                      "code": "generic_decline",
                      "family": "hard_decline",
                      "description": "Transaction failed"
                    },
                    "INS-9": {
                      "code": "",
                      "family": "pending",
                      "description": "Request timeout: result not yet known"
                    },
                    "INS-993": {
                      "code": "transaction_not_allowed",
                      "family": "hard_decline",
                      "description": "Direct debit missing"
                    },
                    "INS-994": {
                      "code": "duplicate_transaction",
                      "family": "hard_decline",
                      "description": "Direct debit already exists"
                    },
                    "INS-995": {
                      "code": "transaction_not_allowed",
                      "family": "hard_decline",
                      "description": "Customer's profile has problems"
                    },
                    "INS-996": {
                      "code": "transaction_not_allowed",
                      "family": "hard_decline",
                      "description": "Customer account status not active"
                    },
                    "INS-997": {
                      "code": "connector_error",
                      "family": "error",
                      "description": "Linking transaction not found"
                    },
                    "INS-998": {
                      "code": "transaction_not_allowed",
                      "family": "hard_decline",
                      "description": "Invalid market"
                    }
                  },
                  "versions": [
                    {
                      "id": "3KWPlh0E8ChIlO54KGMxDffyHBC",
                      "provider": "mpesa",
                      "version": 1,
                      "status": "pending",
                      "reason": "new code in production",
                      "approval_id": "3KWPlkG2ypXCcvR1wlQ7iLTOoS6",
                      "created_by": "key_accounts",
                      "created_at": "2026-10-10T22:15:03.507490707Z"
                    }
                  ],
                  "pending_approvals": [
                    {
                      "id": "3KWPlkG2ypXCcvR1wlQ7iLTOoS6",
                      "kind": "code_mapping.activate",
                      "subject_id": "3KWPlh0E8ChIlO54KGMxDffyHBC",
                      "tenant_id": "platform",
                      "reason": "mpesa codes v1: new code in production",
                      "prepared_by": "key_accounts",
                      "status": "pending",
                      "created_at": "2026-10-10T22:15:03.509405079Z"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Reference"
        ],
        "operationId": "proposeCodeMapping",
        "summary": "Propose a mapping version",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "mpesa"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "entries",
                  "reason"
                ],
                "properties": {
                  "entries": {
                    "type": "object",
                    "additionalProperties": {
                      "$ref": "#/components/schemas/CodeEntry"
                    },
                    "description": "Raw code to KulPay code: the full set of overrides of this version."
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "entries": {
                  "key": {
                    "code": "EDM",
                    "family": "approved",
                    "description": "Pagamento de energia, outubro"
                  }
                },
                "reason": "Pagamento de energia, outubro"
              }
            }
          }
        },
        "description": "A new version, active once a different person approves it (it decides payment outcomes): code_mapping.version_created, then code_mapping.activated. Requires `connectors:write`.",
        "responses": {
          "202": {
            "description": "Pending approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mapping": {
                      "$ref": "#/components/schemas/CodeMapping"
                    },
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    }
                  }
                },
                "example": {
                  "mapping": {
                    "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "provider": "mpesa",
                    "version": 1,
                    "status": "pending",
                    "reason": "Pagamento de energia, outubro",
                    "approval_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "created_by": "string",
                    "created_at": "2026-10-10T09:30:00Z",
                    "activated_at": "2026-10-10T09:30:00Z"
                  },
                  "approval": {
                    "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "kind": "standard",
                    "subject_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                    "reason": "Pagamento de energia, outubro",
                    "prepared_by": "string",
                    "status": "pending",
                    "decided_by": "string",
                    "decision_reason": "Pagamento de energia, outubro",
                    "created_at": "2026-10-10T09:30:00Z",
                    "decided_at": "2026-10-10T09:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "An entry is not valid (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "INS-30: made_up is not a KulPay failure code",
                  "error": {
                    "code": "invalid_request",
                    "message": "INS-30: made_up is not a KulPay failure code"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payout-batches": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "adminListPayoutBatches",
        "summary": "Bank payout batches",
        "description": "The bank payout rail's batches (F2-1), newest first: one per cut-off, mode and currency, with the bank's answers counted. Filter with `mode` and `status`. Requires `admin:read` (the batches belong to the platform, not to a merchant).",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "sent",
                "acknowledged",
                "partially_rejected",
                "rejected",
                "settled"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "debit_account": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "total_minor": {
                            "type": "integer"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "closed",
                              "released",
                              "sent",
                              "acknowledged",
                              "partially_rejected",
                              "rejected",
                              "settled"
                            ]
                          },
                          "execution_date": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "msg_id": {
                            "type": "string",
                            "description": "The pain.001 MsgId"
                          },
                          "file_id": {
                            "type": "string"
                          },
                          "file_sha256": {
                            "type": "string"
                          },
                          "accepted": {
                            "type": "integer"
                          },
                          "rejected": {
                            "type": "integer"
                          },
                          "settled": {
                            "type": "integer"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "closed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "sent_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "acknowledged_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "settled_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "released_by": {
                            "type": "string",
                            "description": "Who approved sending it (`system:test_mode` in test mode)."
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KZPrJTswOevmFFmZGKvJjEgsgW",
                      "mode": "live",
                      "debit_account": "000300010000007777720",
                      "currency": "MZN",
                      "count": 3,
                      "total_minor": 200050,
                      "status": "settled",
                      "execution_date": "2026-10-12T00:00:00Z",
                      "msg_id": "KP3KZPrJTswOevmFFmZGKvJjEgsgW",
                      "file_id": "3KZPrK0aDqk4JqNw0fiq9UuA9Fs",
                      "file_sha256": "b3c1\u2026",
                      "accepted": 2,
                      "rejected": 1,
                      "settled": 2,
                      "created_at": "2026-10-12T08:30:00Z",
                      "closed_at": "2026-10-12T08:30:00Z",
                      "sent_at": "2026-10-12T08:30:02Z",
                      "acknowledged_at": "2026-10-12T09:05:11Z",
                      "settled_at": "2026-10-12T15:40:27Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payout-batches/{id}/file": {
      "get": {
        "tags": [
          "Payouts"
        ],
        "operationId": "adminPayoutBatchFile",
        "summary": "A batch's pain.001",
        "description": "The pain.001.001.09 exactly as it was sent to the bank; X-Content-SHA256 is its hash, as recorded on the batch. Requires `admin:read` (the batches belong to the platform, not to a merchant).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The XML file.",
            "headers": {
              "X-Content-SHA256": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                },
                "example": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Document xmlns=\"urn:iso:std:iso:20022:tech:xsd:pain.001.001.09\">\u2026</Document>"
              }
            }
          },
          "404": {
            "description": "No such batch.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payout batch",
                  "error": {
                    "code": "not_found",
                    "message": "no such payout batch"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payout-batches/{id}/release": {
      "post": {
        "tags": [
          "Payouts"
        ],
        "operationId": "adminReleasePayoutBatch",
        "summary": "Ask to release a batch (maker)",
        "description": "A live batch closes at its cut-off but leaves only when a second person approves its release: this prepares a `payout.batch.release` approval that a different person decides (POST /v1/admin/approvals/{id}/decide; the requester gets 403 `same_approver`). Approved, the batch is `released` and the next run sends it. Only a `closed` batch can be released: one already released or sent is refused (409 `invalid_state`), and so is a second request while one waits. The release is refused when it would take the debit account past its daily limit (422 `limit_exceeded`, `rule` `debit_daily`), checked again at approval. Test-mode batches are released at once by `system:test_mode`. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "the 10:00 batch, checked against the treasury plan"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Requested; a second person must approve it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KZYtg5YPKn7YqshJrVYF5d214M",
                    "kind": "payout.batch.release",
                    "subject_id": "3KZYtgkUxOWOVW8YRJ0xUgnJ2ZO",
                    "tenant_id": "platform",
                    "reason": "release batch KP20261012100000A1: 2 payout(s), 1700.00 MZN \u2014 the 10:00 batch, checked against the treasury plan",
                    "prepared_by": "key_treasury_ana",
                    "status": "pending",
                    "created_at": "2026-10-12T08:00:05Z"
                  },
                  "message": "release requested; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "400": {
            "description": "No reason (`invalid_request`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a reason is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a reason is required"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such batch.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such payout batch",
                  "error": {
                    "code": "not_found",
                    "message": "no such payout batch"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The batch is not closed (already released or sent), or a release already waits (`invalid_state`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid state",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "only a closed batch can be released; a released or sent batch is never sent again",
                  "error": {
                    "code": "invalid_state",
                    "message": "only a closed batch can be released; a released or sent batch is never sent again"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`limit_exceeded`: the debit account's daily limit (`rule` `debit_daily`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Limit exceeded",
                  "status": 422,
                  "code": "limit_exceeded",
                  "detail": "releasing 500.00 MZN would take the debit account past its daily limit of 2000 MZN (1700.00 released today)",
                  "rule": "debit_daily",
                  "error": {
                    "code": "limit_exceeded",
                    "message": "releasing 500.00 MZN would take the debit account past its daily limit of 2000 MZN (1700.00 released today)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/remittances/{id}/pay": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "remittancePay",
        "summary": "Pay a remittance by the bank rail (maker)",
        "description": "Asks a second person to pay a live remittance by the bank rail (POST /v1/admin/approvals/{id}/decide; the requester gets 403 `same_approver`). Approved, a bank transfer of the net to the biller's settlement account (`remit_account`, `remit_holder`) joins the next batch to the sponsor bank: the remittance is `paying` (`payout_id`), then `paid` with `paid_by: bank_rail` and `bank_reference` the transfer's EndToEndId when the bank's camt.054 debits it, and the biller receives `bill_remittance.paid`. If the bank rejects or returns the transfer the remittance is `pending` again (`bill_remittance.payment_failed`). 400 for a test-mode remittance (paid by simulation), one not pending, a biller without a NIB and holder, or the rail not enabled. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "daily remittance"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPlDhVsFz6D7Nlw0sljeII8l1",
                    "kind": "bill.remittance_pay",
                    "subject_id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                    "tenant_id": "platform",
                    "reason": "pay remittance 3KWPlF7nZdWEWid2kjF1ZQJ718o: 1500.00 MZN to AGUAS *****************3585 \u2014 daily",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:14:59.570339309Z"
                  },
                  "message": "payment requested; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a test-mode remittance is paid by simulation, not by treasury",
                  "error": {
                    "code": "invalid_request",
                    "message": "a test-mode remittance is paid by simulation, not by treasury"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such remittance.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/billers/{code}/remit-account": {
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putBillerRemitAccount",
        "summary": "Change a biller's settlement account (maker)",
        "description": "Where a biller is paid is a fraud target: a change waits for a second person (POST /v1/admin/approvals/{id}/decide). `account` is the NIB (21 digits, check digits verified; spaces ignored), `holder` the account holder as on the bank's records, `bank` optional (the NIB's bank code by default). Approved, the account is set, `biller.updated` recorded, and the biller's support email and KulPay's compliance contacts are told by email. Past draft, PATCH /v1/admin/billers/{code} refuses `remit_bank` and `remit_account`. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account",
                  "holder",
                  "reason"
                ],
                "properties": {
                  "bank": {
                    "type": "string"
                  },
                  "account": {
                    "type": "string",
                    "description": "The NIB."
                  },
                  "holder": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "bank": "BCI",
                "account": "000300010000001234585",
                "holder": "\u00c1guas da Regi\u00e3o de Maputo, SA",
                "reason": "the biller's bank letter"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KWPlDhVsFz6D7Nlw0sljeII8l1",
                    "kind": "biller.remit_account",
                    "subject_id": "3KWPlF7nZdWEWid2kjF1ZQJ718o",
                    "tenant_id": "platform",
                    "payload": "{\"bank\":\"BCI\",\"account\":\"000300010000001234585\",\"holder\":\"\u00c1guas da Regi\u00e3o de Maputo, SA\"}",
                    "reason": "settlement account of AGUAS to *****************4585 (\u00c1guas da Regi\u00e3o de Maputo, SA): the biller's bank letter",
                    "prepared_by": "key_accounts",
                    "status": "pending",
                    "created_at": "2026-10-10T22:14:59.570339309Z"
                  },
                  "message": "change requested; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: not a NIB, no holder, or a change already waiting.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "account is the biller's NIB: 21 digits whose check digits verify",
                  "error": {
                    "code": "invalid_request",
                    "message": "account is the biller's NIB: 21 digits whose check digits verify"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such biller, product or supply connector",
                  "error": {
                    "code": "not_found",
                    "message": "no such biller, product or supply connector"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/disputes": {
      "get": {
        "tags": [
          "Disputes"
        ],
        "operationId": "listDisputes",
        "summary": "List disputes",
        "description": "The account's disputes, those waiting first, the soonest deadline first. A dispute is opened by the acquirer (Stripe charge.dispute.created: `source: provider`) or by an operator on a rail without acquirer disputes (`source: operator`); its amount moves from the available balance to disputes_hold. Answer with evidence before `deadline`: it is `under_review` until decided. `won` puts the amount back; `lost` keeps it and charges the chargeback fee of the commission schedule (a rule of type DISPUTE). Reminders (`dispute.deadline_approaching`) go out 5 days and 1 day before the deadline; a dispute unanswered by then is lost. Webhooks: dispute.created, dispute.evidence_submitted, dispute.deadline_approaching, dispute.won, dispute.lost. Requires `payments:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "needs_response",
                "under_review",
                "won",
                "lost"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "dispute_ref": {
                            "type": "string",
                            "description": "The acquirer's dispute id (Stripe dp_...); empty for an operator's dispute."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "needs_response",
                              "under_review",
                              "won",
                              "lost"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "fraudulent",
                              "unrecognized",
                              "duplicate",
                              "product_not_received",
                              "product_unacceptable",
                              "subscription_canceled",
                              "credit_not_processed",
                              "general"
                            ]
                          },
                          "provider_reason": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "provider",
                              "operator"
                            ]
                          },
                          "note": {
                            "type": "string"
                          },
                          "deadline": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Answer before it, or the dispute is lost."
                          },
                          "evidence": {
                            "type": "object",
                            "description": "The evidence submitted: text fields and the documents (file ids)."
                          },
                          "evidence_submitted_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "fee": {
                            "type": "string",
                            "description": "The chargeback fee charged when lost (a DISPUTE rule of the commission schedule)."
                          },
                          "close_reason": {
                            "type": "string"
                          },
                          "closed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KZf0Qd9XyFhR2m8AqgP1sLwZbN",
                      "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                      "account_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                      "mode": "live",
                      "dispute_ref": "dp_1Q8xYz",
                      "status": "needs_response",
                      "amount": "400",
                      "currency": "MZN",
                      "reason": "product_not_received",
                      "provider_reason": "product_not_received",
                      "source": "provider",
                      "deadline": "2026-10-22T23:59:59Z",
                      "fee": "0",
                      "created_at": "2026-10-12T09:14:03Z",
                      "updated_at": "2026-10-12T09:14:03Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}": {
      "get": {
        "tags": [
          "Disputes"
        ],
        "operationId": "getDispute",
        "summary": "Get a dispute",
        "description": "One of the account's disputes. Requires `payments:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dispute.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "payment_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "dispute_ref": {
                      "type": "string",
                      "description": "The acquirer's dispute id (Stripe dp_...); empty for an operator's dispute."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "needs_response",
                        "under_review",
                        "won",
                        "lost"
                      ]
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "fraudulent",
                        "unrecognized",
                        "duplicate",
                        "product_not_received",
                        "product_unacceptable",
                        "subscription_canceled",
                        "credit_not_processed",
                        "general"
                      ]
                    },
                    "provider_reason": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "provider",
                        "operator"
                      ]
                    },
                    "note": {
                      "type": "string"
                    },
                    "deadline": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Answer before it, or the dispute is lost."
                    },
                    "evidence": {
                      "type": "object",
                      "description": "The evidence submitted: text fields and the documents (file ids)."
                    },
                    "evidence_submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "fee": {
                      "type": "string",
                      "description": "The chargeback fee charged when lost (a DISPUTE rule of the commission schedule)."
                    },
                    "close_reason": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZf0Qd9XyFhR2m8AqgP1sLwZbN",
                  "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                  "account_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                  "mode": "live",
                  "dispute_ref": "dp_1Q8xYz",
                  "status": "needs_response",
                  "amount": "400",
                  "currency": "MZN",
                  "reason": "product_not_received",
                  "provider_reason": "product_not_received",
                  "source": "provider",
                  "deadline": "2026-10-22T23:59:59Z",
                  "fee": "0",
                  "created_at": "2026-10-12T09:14:03Z",
                  "updated_at": "2026-10-12T09:14:03Z"
                }
              }
            }
          },
          "404": {
            "description": "No such dispute (or another account's).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such dispute",
                  "error": {
                    "code": "not_found",
                    "message": "no such dispute"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/evidence": {
      "post": {
        "tags": [
          "Disputes"
        ],
        "operationId": "submitDisputeEvidence",
        "summary": "Answer a dispute with evidence",
        "description": "Submits the evidence to the acquirer and moves the dispute to `under_review` (`dispute.evidence_submitted`). `text` takes the card networks' evidence fields (product_description, customer_name, customer_email_address, billing_address, service_date, shipping_date, shipping_carrier, shipping_tracking_number, refund_policy_disclosure, refund_refusal_explanation, cancellation_rebuttal, duplicate_charge_explanation, access_activity_log, uncategorized_text); `files` attaches documents uploaded first to the Files API with purpose `dispute_evidence`, each as one of receipt, customer_communication, customer_signature, refund_policy, cancellation_policy, service_documentation, shipping_documentation, duplicate_charge_documentation, uncategorized_file. Only once: a retry with the same Idempotency-Key returns the dispute. Requires `payments:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "field",
                        "file_id"
                      ],
                      "properties": {
                        "field": {
                          "type": "string"
                        },
                        "file_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "text": {
                  "product_description": "Two bags of cement, delivered on 3 October to the address on the order"
                },
                "files": [
                  {
                    "field": "customer_communication",
                    "file_id": "3KZf1aB7cD9eF0gH2iJ4kL6mN8p"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The dispute, under review.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "payment_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "dispute_ref": {
                      "type": "string",
                      "description": "The acquirer's dispute id (Stripe dp_...); empty for an operator's dispute."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "needs_response",
                        "under_review",
                        "won",
                        "lost"
                      ]
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "fraudulent",
                        "unrecognized",
                        "duplicate",
                        "product_not_received",
                        "product_unacceptable",
                        "subscription_canceled",
                        "credit_not_processed",
                        "general"
                      ]
                    },
                    "provider_reason": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "provider",
                        "operator"
                      ]
                    },
                    "note": {
                      "type": "string"
                    },
                    "deadline": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Answer before it, or the dispute is lost."
                    },
                    "evidence": {
                      "type": "object",
                      "description": "The evidence submitted: text fields and the documents (file ids)."
                    },
                    "evidence_submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "fee": {
                      "type": "string",
                      "description": "The chargeback fee charged when lost (a DISPUTE rule of the commission schedule)."
                    },
                    "close_reason": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZf0Qd9XyFhR2m8AqgP1sLwZbN",
                  "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                  "account_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                  "mode": "live",
                  "dispute_ref": "dp_1Q8xYz",
                  "status": "under_review",
                  "amount": "400",
                  "currency": "MZN",
                  "reason": "product_not_received",
                  "provider_reason": "product_not_received",
                  "source": "provider",
                  "deadline": "2026-10-22T23:59:59Z",
                  "fee": "0",
                  "created_at": "2026-10-12T09:14:03Z",
                  "updated_at": "2026-10-12T09:14:03Z",
                  "evidence_submitted_at": "2026-10-14T10:02:44Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: no evidence, an unknown field, or a file that is not this account's dispute evidence.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "unknown evidence field \"favourite_colour\"",
                  "error": {
                    "code": "invalid_request",
                    "message": "unknown evidence field \"favourite_colour\""
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such dispute (or another account's).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such dispute",
                  "error": {
                    "code": "not_found",
                    "message": "no such dispute"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invalid_state`: no longer waiting for a response.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid state",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "The dispute is no longer waiting for a response.",
                  "error": {
                    "code": "invalid_state",
                    "message": "The dispute is no longer waiting for a response."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/disputes/{id}/accept": {
      "post": {
        "tags": [
          "Disputes"
        ],
        "operationId": "acceptDispute",
        "summary": "Concede a dispute",
        "description": "The merchant does not contest: the dispute is lost now (`dispute.lost`, close_reason \"accepted by the merchant\"), with the chargeback fee. Requires `payments:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dispute, lost.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "payment_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "dispute_ref": {
                      "type": "string",
                      "description": "The acquirer's dispute id (Stripe dp_...); empty for an operator's dispute."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "needs_response",
                        "under_review",
                        "won",
                        "lost"
                      ]
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "fraudulent",
                        "unrecognized",
                        "duplicate",
                        "product_not_received",
                        "product_unacceptable",
                        "subscription_canceled",
                        "credit_not_processed",
                        "general"
                      ]
                    },
                    "provider_reason": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "provider",
                        "operator"
                      ]
                    },
                    "note": {
                      "type": "string"
                    },
                    "deadline": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Answer before it, or the dispute is lost."
                    },
                    "evidence": {
                      "type": "object",
                      "description": "The evidence submitted: text fields and the documents (file ids)."
                    },
                    "evidence_submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "fee": {
                      "type": "string",
                      "description": "The chargeback fee charged when lost (a DISPUTE rule of the commission schedule)."
                    },
                    "close_reason": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZf0Qd9XyFhR2m8AqgP1sLwZbN",
                  "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                  "account_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                  "mode": "live",
                  "dispute_ref": "dp_1Q8xYz",
                  "status": "lost",
                  "amount": "400",
                  "currency": "MZN",
                  "reason": "product_not_received",
                  "provider_reason": "product_not_received",
                  "source": "provider",
                  "deadline": "2026-10-22T23:59:59Z",
                  "fee": "150",
                  "created_at": "2026-10-12T09:14:03Z",
                  "updated_at": "2026-10-12T09:14:03Z",
                  "close_reason": "accepted by the merchant",
                  "closed_at": "2026-10-13T08:00:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such dispute (or another account's).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such dispute",
                  "error": {
                    "code": "not_found",
                    "message": "no such dispute"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invalid_state`: no longer waiting for a response.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid state",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "The dispute is no longer waiting for a response.",
                  "error": {
                    "code": "invalid_state",
                    "message": "The dispute is no longer waiting for a response."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/disputes": {
      "get": {
        "tags": [
          "Disputes"
        ],
        "operationId": "adminListDisputes",
        "summary": "List every dispute",
        "description": "Every account's disputes, waiting first. Requires `admin:read`.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "payment_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "dispute_ref": {
                            "type": "string",
                            "description": "The acquirer's dispute id (Stripe dp_...); empty for an operator's dispute."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "needs_response",
                              "under_review",
                              "won",
                              "lost"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "enum": [
                              "fraudulent",
                              "unrecognized",
                              "duplicate",
                              "product_not_received",
                              "product_unacceptable",
                              "subscription_canceled",
                              "credit_not_processed",
                              "general"
                            ]
                          },
                          "provider_reason": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "provider",
                              "operator"
                            ]
                          },
                          "note": {
                            "type": "string"
                          },
                          "deadline": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Answer before it, or the dispute is lost."
                          },
                          "evidence": {
                            "type": "object",
                            "description": "The evidence submitted: text fields and the documents (file ids)."
                          },
                          "evidence_submitted_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "fee": {
                            "type": "string",
                            "description": "The chargeback fee charged when lost (a DISPUTE rule of the commission schedule)."
                          },
                          "close_reason": {
                            "type": "string"
                          },
                          "closed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KZf0Qd9XyFhR2m8AqgP1sLwZbN",
                      "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                      "account_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                      "mode": "live",
                      "dispute_ref": "dp_1Q8xYz",
                      "status": "needs_response",
                      "amount": "400",
                      "currency": "MZN",
                      "reason": "product_not_received",
                      "provider_reason": "product_not_received",
                      "source": "provider",
                      "deadline": "2026-10-22T23:59:59Z",
                      "fee": "0",
                      "created_at": "2026-10-12T09:14:03Z",
                      "updated_at": "2026-10-12T09:14:03Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Disputes"
        ],
        "operationId": "adminOpenDispute",
        "summary": "Open a dispute (maker)",
        "description": "For a rail without acquirer disputes (a complaint on M-Pesa or e-Mola): prepares a `dispute.open` approval a different person decides (POST /v1/admin/approvals/{id}/decide). Approved, the dispute opens (`source: operator`), the amount is held and the payment becomes `disputed`. Only a succeeded payment can be disputed; `amount` defaults to the payment's, `deadline_days` to 7. Requires `admin:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payment_id",
                  "reason"
                ],
                "properties": {
                  "payment_id": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "string"
                  },
                  "reason_code": {
                    "type": "string"
                  },
                  "deadline_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 120
                  },
                  "note": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string",
                    "description": "Why the request is made, for the approver."
                  }
                }
              },
              "example": {
                "payment_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                "reason_code": "fraudulent",
                "deadline_days": 10,
                "note": "the payer says the phone was stolen",
                "reason": "complaint 4471"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared; a second person must approve it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KZf2xY4zA6bC8dE0fG2hI4jK6l",
                    "kind": "dispute.open",
                    "subject_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                    "tenant_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                    "reason": "complaint 4471",
                    "prepared_by": "key_ops_ana",
                    "status": "pending",
                    "created_at": "2026-10-12T09:20:00Z"
                  },
                  "message": "dispute prepared; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "only a succeeded payment can be disputed (this one is failed)",
                  "error": {
                    "code": "invalid_request",
                    "message": "only a succeeded payment can be disputed (this one is failed)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/disputes/{id}/decide": {
      "post": {
        "tags": [
          "Disputes"
        ],
        "operationId": "adminDecideDispute",
        "summary": "Rule on a dispute (maker)",
        "description": "An operator's ruling, `won` or `lost`, for a dispute no acquirer decides: prepares a `dispute.decide` approval for a second person. Approved, won puts the amount back and the payment is `succeeded` again; lost keeps it and charges the chargeback fee. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcome",
                  "reason"
                ],
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "won",
                      "lost"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "outcome": "won",
                "reason": "the payer recognised the payment"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared; a second person must approve it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KZf2xY4zA6bC8dE0fG2hI4jK6l",
                    "kind": "dispute.decide",
                    "subject_id": "3KZezI1uT8vXcW0qJdYp7nRkLmA",
                    "tenant_id": "3KZezGk2wQ4eVb9nC6xHs1TrPuD",
                    "reason": "complaint 4471",
                    "prepared_by": "key_ops_ana",
                    "status": "pending",
                    "created_at": "2026-10-12T09:20:00Z"
                  },
                  "message": "ruling prepared; a second person must approve it (POST /v1/admin/approvals/{id}/decide)"
                }
              }
            }
          },
          "404": {
            "description": "No such dispute (or another account's).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such dispute",
                  "error": {
                    "code": "not_found",
                    "message": "no such dispute"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`invalid_state`: no longer waiting for a response.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid state",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "The dispute is no longer waiting for a response.",
                  "error": {
                    "code": "invalid_state",
                    "message": "The dispute is no longer waiting for a response."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/regulatory/runs/{id}/receipt": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "regulatoryReceipt",
        "summary": "Keep the regulator's receipt",
        "description": "The regulator's receipt of a submitted report, as the multipart field `file` (10 MB at most): kept write-once beside the signed file, once (`regulatory_report.receipt_attached`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The run, with its receipt.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "template_id": {
                      "type": "string"
                    },
                    "template_version": {
                      "type": "integer"
                    },
                    "code": {
                      "type": "string"
                    },
                    "period_start": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "period_end": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "trigger": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "generated",
                        "blocked",
                        "pending",
                        "submitted"
                      ]
                    },
                    "reconciled": {
                      "type": "boolean"
                    },
                    "rows": {
                      "type": "integer"
                    },
                    "totals": {
                      "type": "string"
                    },
                    "ledger_totals": {
                      "type": "string"
                    },
                    "format": {
                      "type": "string"
                    },
                    "file_id": {
                      "type": "string"
                    },
                    "sha256": {
                      "type": "string"
                    },
                    "signature": {
                      "type": "string"
                    },
                    "signing_key_id": {
                      "type": "string"
                    },
                    "submitted_by": {
                      "type": "string"
                    },
                    "submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "channel": {
                      "type": "string"
                    },
                    "submission_reference": {
                      "type": "string"
                    },
                    "receipt_file_id": {
                      "type": "string"
                    },
                    "retain_until": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KLkM29zNWk8XOFM5SgHNsugPV9",
                  "code": "BM-PAY-DAILY",
                  "status": "submitted",
                  "channel": "portal",
                  "submission_reference": "BDM-2026-10-0042",
                  "approved_by": "key_ops_rui",
                  "receipt_file_id": "3KZk1Pq3Rs5Tu7Vw9Xy1Za3Bc5D",
                  "retain_until": "2036-10-12T08:05:00Z"
                }
              }
            }
          },
          "409": {
            "description": "Not submitted yet, or the receipt is already kept (`not_submittable`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_submittable",
                  "detail": "the receipt is already kept",
                  "error": {
                    "code": "not_submittable",
                    "message": "the receipt is already kept"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/compliance/cases": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "listComplianceCases",
        "summary": "List suspicious-activity cases",
        "description": "Suspicious-activity cases (F3-8) are opened by an operator or by a risk rule marked `suspicion`, investigated (notes), and decided \u2014 report to GIFiM or not \u2014 with a justification by one compliance officer and approval by another. They belong to the platform: only keys with `compliance:read`/`compliance:write` reach them (admin:* does not), and nothing about a case reaches the customer or the partner (no tipping-off). Requires `compliance:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`, open first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "subject_type": {
                            "type": "string",
                            "enum": [
                              "transaction",
                              "payment",
                              "payout",
                              "account",
                              "cash_deposit"
                            ]
                          },
                          "subject_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "operator",
                              "risk_rule"
                            ]
                          },
                          "rule_id": {
                            "type": "string"
                          },
                          "summary": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "decision_due",
                              "reported",
                              "closed"
                            ]
                          },
                          "notes": {
                            "type": "string"
                          },
                          "decision": {
                            "type": "string",
                            "enum": [
                              "report",
                              "no_report"
                            ]
                          },
                          "decision_reason": {
                            "type": "string"
                          },
                          "decided_by": {
                            "type": "string"
                          },
                          "approved_by": {
                            "type": "string"
                          },
                          "report_reference": {
                            "type": "string"
                          },
                          "opened_by": {
                            "type": "string"
                          },
                          "closed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KZm0Cs1Dt2Eu3Fv4Gw5Hx6Iy7J",
                      "subject_type": "transaction",
                      "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                      "account_id": "3KZlyE2fG4hI6jK8lM0nO2pQ4rS",
                      "source": "risk_rule",
                      "rule_id": "structuring",
                      "summary": "risk rule structuring fired (review): amount 950 MZN at or above 900",
                      "status": "open",
                      "opened_by": "system:risk",
                      "created_at": "2026-10-12T09:00:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "openComplianceCase",
        "summary": "Open a case",
        "description": "Requires `compliance:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject_type",
                  "subject_id",
                  "summary"
                ],
                "properties": {
                  "subject_type": {
                    "type": "string"
                  },
                  "subject_id": {
                    "type": "string"
                  },
                  "account_id": {
                    "type": "string"
                  },
                  "summary": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "subject_type": "cash_deposit",
                "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                "summary": "three deposits of 240 000 MZN in a day by the same depositor"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string",
                      "enum": [
                        "transaction",
                        "payment",
                        "payout",
                        "account",
                        "cash_deposit"
                      ]
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "operator",
                        "risk_rule"
                      ]
                    },
                    "rule_id": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "decision_due",
                        "reported",
                        "closed"
                      ]
                    },
                    "notes": {
                      "type": "string"
                    },
                    "decision": {
                      "type": "string",
                      "enum": [
                        "report",
                        "no_report"
                      ]
                    },
                    "decision_reason": {
                      "type": "string"
                    },
                    "decided_by": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "report_reference": {
                      "type": "string"
                    },
                    "opened_by": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZm0Cs1Dt2Eu3Fv4Gw5Hx6Iy7J",
                  "subject_type": "transaction",
                  "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                  "account_id": "3KZlyE2fG4hI6jK8lM0nO2pQ4rS",
                  "source": "risk_rule",
                  "rule_id": "structuring",
                  "summary": "risk rule structuring fired (review): amount 950 MZN at or above 900",
                  "status": "open",
                  "opened_by": "system:risk",
                  "created_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/compliance/cases/{id}": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "getComplianceCase",
        "summary": "Get a case",
        "description": "Requires `compliance:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string",
                      "enum": [
                        "transaction",
                        "payment",
                        "payout",
                        "account",
                        "cash_deposit"
                      ]
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "operator",
                        "risk_rule"
                      ]
                    },
                    "rule_id": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "decision_due",
                        "reported",
                        "closed"
                      ]
                    },
                    "notes": {
                      "type": "string"
                    },
                    "decision": {
                      "type": "string",
                      "enum": [
                        "report",
                        "no_report"
                      ]
                    },
                    "decision_reason": {
                      "type": "string"
                    },
                    "decided_by": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "report_reference": {
                      "type": "string"
                    },
                    "opened_by": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZm0Cs1Dt2Eu3Fv4Gw5Hx6Iy7J",
                  "subject_type": "transaction",
                  "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                  "account_id": "3KZlyE2fG4hI6jK8lM0nO2pQ4rS",
                  "source": "risk_rule",
                  "rule_id": "structuring",
                  "summary": "risk rule structuring fired (review): amount 950 MZN at or above 900",
                  "status": "open",
                  "opened_by": "system:risk",
                  "created_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/compliance/cases/{id}/notes": {
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "noteComplianceCase",
        "summary": "Record a step of the investigation",
        "description": "Requires `compliance:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "text": "called the partner's compliance officer"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string",
                      "enum": [
                        "transaction",
                        "payment",
                        "payout",
                        "account",
                        "cash_deposit"
                      ]
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "operator",
                        "risk_rule"
                      ]
                    },
                    "rule_id": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "decision_due",
                        "reported",
                        "closed"
                      ]
                    },
                    "notes": {
                      "type": "string"
                    },
                    "decision": {
                      "type": "string",
                      "enum": [
                        "report",
                        "no_report"
                      ]
                    },
                    "decision_reason": {
                      "type": "string"
                    },
                    "decided_by": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "report_reference": {
                      "type": "string"
                    },
                    "opened_by": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZm0Cs1Dt2Eu3Fv4Gw5Hx6Iy7J",
                  "subject_type": "transaction",
                  "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                  "account_id": "3KZlyE2fG4hI6jK8lM0nO2pQ4rS",
                  "source": "risk_rule",
                  "rule_id": "structuring",
                  "summary": "risk rule structuring fired (review): amount 950 MZN at or above 900",
                  "status": "open",
                  "opened_by": "system:risk",
                  "created_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/compliance/cases/{id}/decision": {
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "decideComplianceCase",
        "summary": "Decide a case (maker)",
        "description": "`report` (with GIFiM's reference of the communication) or `no_report`, with a justification of at least 10 characters; a second compliance officer approves it (`/approve`). Requires `compliance:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "decision",
                  "reason"
                ],
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "report",
                      "no_report"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "decision": "report",
                "reason": "structuring below the review threshold over three days",
                "reference": "GIFIM-2026-0099"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Prepared.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval_id": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "approval_id": "3KZm1Ab2Cd3Ef4Gh5Ij6Kl7Mn8O",
                  "message": "decision prepared; a second compliance officer must approve it (POST /v1/admin/compliance/cases/{id}/approve)"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/compliance/cases/{id}/approve": {
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "approveComplianceCase",
        "summary": "Approve a case's decision (checker)",
        "description": "The second compliance officer; the one who decided gets 403 `same_approver`. Approved, the case is `reported` or `closed`. Requires `compliance:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "approve",
                  "reason"
                ],
                "properties": {
                  "approve": {
                    "type": "boolean"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "approve": true,
                "reason": "agreed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subject_type": {
                      "type": "string",
                      "enum": [
                        "transaction",
                        "payment",
                        "payout",
                        "account",
                        "cash_deposit"
                      ]
                    },
                    "subject_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "operator",
                        "risk_rule"
                      ]
                    },
                    "rule_id": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "open",
                        "decision_due",
                        "reported",
                        "closed"
                      ]
                    },
                    "notes": {
                      "type": "string"
                    },
                    "decision": {
                      "type": "string",
                      "enum": [
                        "report",
                        "no_report"
                      ]
                    },
                    "decision_reason": {
                      "type": "string"
                    },
                    "decided_by": {
                      "type": "string"
                    },
                    "approved_by": {
                      "type": "string"
                    },
                    "report_reference": {
                      "type": "string"
                    },
                    "opened_by": {
                      "type": "string"
                    },
                    "closed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZm0Cs1Dt2Eu3Fv4Gw5Hx6Iy7J",
                  "subject_type": "transaction",
                  "subject_id": "3KZlzP9qR1sT3uV5wX7yZ9aB1cD",
                  "account_id": "3KZlyE2fG4hI6jK8lM0nO2pQ4rS",
                  "source": "risk_rule",
                  "rule_id": "structuring",
                  "summary": "risk rule structuring fired (review): amount 950 MZN at or above 900",
                  "status": "open",
                  "opened_by": "system:risk",
                  "created_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/reports/daily": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerDailyReport",
        "summary": "Send the day's collections report",
        "description": "The biller's report of the payments it received through KulPay on `day` (F3-12), as CSV with the header `request_id, customer_reference, amount, currency, paid_at, supplier_reference` or JSON `{\"payments\": [...]}` with the same fields; `request_id` (the bill payment id, or the request id KulPay sent) or `supplier_reference` on each payment, 10 000 at most. It is reconciled at once with the biller's bill payments of the day; the answer lists the breaks (supplier_only, kulpay_only, amount_mismatch, review_found, failed_but_delivered), also shown in the portal and to the operators (`bill_recon.biller_report`). Requires `invoices:write`.",
        "parameters": [
          {
            "name": "day",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Default: yesterday."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/csv": {
              "schema": {
                "type": "string"
              },
              "example": "request_id,customer_reference,amount,currency,paid_at,supplier_reference\n3KZmxE1fG3hI5jK7lM9nO1pQ3rS,CL-1,1500.00,MZN,2026-10-09T09:00:00Z,AG-001\n"
            },
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "payments": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string"
                        },
                        "customer_reference": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "paid_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "supplier_reference": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "payments": [
                  {
                    "request_id": "3KZmxE1fG3hI5jK7lM9nO1pQ3rS",
                    "customer_reference": "CL-1",
                    "amount": "1500.00",
                    "currency": "MZN",
                    "paid_at": "2026-10-09T09:00:00Z",
                    "supplier_reference": "AG-001"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The run with its breaks.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "biller_id": {
                      "type": "string"
                    },
                    "day": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "source": {
                      "type": "string"
                    },
                    "supplier_count": {
                      "type": "integer"
                    },
                    "kulpay_count": {
                      "type": "integer"
                    },
                    "matched": {
                      "type": "integer"
                    },
                    "breaks": {
                      "type": "integer"
                    },
                    "connector": {
                      "type": "string"
                    },
                    "breaks_list": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "supplier_only",
                              "kulpay_only",
                              "amount_mismatch",
                              "review_found",
                              "failed_but_delivered"
                            ]
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "request_id": {
                            "type": "string"
                          },
                          "supplier_reference": {
                            "type": "string"
                          },
                          "customer_reference": {
                            "type": "string"
                          },
                          "supplier_amount_minor": {
                            "type": "integer"
                          },
                          "kulpay_amount_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KZn0Bv2Cw4Dx6Ey8Fz0Ga2Hb4I",
                  "biller_id": "3KZmzQ1rS3tU5vW7xY9zA1bC3dE",
                  "day": "2026-10-08T22:00:00Z",
                  "source": "biller",
                  "supplier_count": 2,
                  "kulpay_count": 2,
                  "matched": 1,
                  "breaks": 2,
                  "connector": "AGUAS",
                  "breaks_list": [
                    {
                      "kind": "supplier_only",
                      "request_id": "unknown-77",
                      "supplier_reference": "AG-002",
                      "customer_reference": "CL-9",
                      "supplier_amount_minor": 30000
                    },
                    {
                      "kind": "kulpay_only",
                      "bill_payment_id": "3KZmyF2gH4iJ6kL8mN0oP2qR4sT",
                      "customer_reference": "CL-2",
                      "kulpay_amount_minor": 50000
                    }
                  ],
                  "created_at": "2026-10-09T06:00:00Z"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerReportRuns",
        "summary": "List the daily report runs",
        "description": "The biller's report runs, newest first, with their breaks. Requires `invoices:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "biller_id": {
                            "type": "string"
                          },
                          "day": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "source": {
                            "type": "string"
                          },
                          "supplier_count": {
                            "type": "integer"
                          },
                          "kulpay_count": {
                            "type": "integer"
                          },
                          "matched": {
                            "type": "integer"
                          },
                          "breaks": {
                            "type": "integer"
                          },
                          "connector": {
                            "type": "string"
                          },
                          "breaks_list": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "kind": {
                                  "type": "string",
                                  "enum": [
                                    "supplier_only",
                                    "kulpay_only",
                                    "amount_mismatch",
                                    "review_found",
                                    "failed_but_delivered"
                                  ]
                                },
                                "bill_payment_id": {
                                  "type": "string"
                                },
                                "request_id": {
                                  "type": "string"
                                },
                                "supplier_reference": {
                                  "type": "string"
                                },
                                "customer_reference": {
                                  "type": "string"
                                },
                                "supplier_amount_minor": {
                                  "type": "integer"
                                },
                                "kulpay_amount_minor": {
                                  "type": "integer"
                                }
                              }
                            }
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KZn0Bv2Cw4Dx6Ey8Fz0Ga2Hb4I",
                      "biller_id": "3KZmzQ1rS3tU5vW7xY9zA1bC3dE",
                      "day": "2026-10-08T22:00:00Z",
                      "source": "biller",
                      "supplier_count": 2,
                      "kulpay_count": 2,
                      "matched": 1,
                      "breaks": 2,
                      "connector": "AGUAS",
                      "breaks_list": [
                        {
                          "kind": "supplier_only",
                          "request_id": "unknown-77",
                          "supplier_reference": "AG-002",
                          "customer_reference": "CL-9",
                          "supplier_amount_minor": 30000
                        },
                        {
                          "kind": "kulpay_only",
                          "bill_payment_id": "3KZmyF2gH4iJ6kL8mN0oP2qR4sT",
                          "customer_reference": "CL-2",
                          "kulpay_amount_minor": 50000
                        }
                      ],
                      "created_at": "2026-10-09T06:00:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/notices": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getNoticeSettings",
        "summary": "Invoice notices to your payers",
        "description": "KulPay can send the payers you enrolled with their consent (lawful_basis consent, consent recorded) their invoice notices itself (F3-15): a new invoice, the reminder three days before it is due, and the payment confirmation \u2014 by SMS when the enrolment has a phone number, by email otherwise, in the payer's language. Never in the quiet hours (a notice written from 21:00 to 08:00 Maputo goes out at 08:00), and at most `daily_cap` a day per payer. Every SMS ends with how to reply STOP and every email with a link that stops them; a stop is recorded (`bill_payer.notices_stopped`) and shown on GET /v1/biller/payers. Payers enrolled on any other basis receive nothing. Channels keep receiving their webhooks either way. Requires `payers:write`.",
        "responses": {
          "200": {
            "description": "The settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notify_payers": {
                      "type": "boolean"
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "kind",
                          "channel",
                          "language",
                          "body"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "invoice_new",
                              "invoice_reminder",
                              "invoice_paid"
                            ]
                          },
                          "channel": {
                            "type": "string",
                            "enum": [
                              "sms",
                              "email"
                            ]
                          },
                          "language": {
                            "type": "string",
                            "enum": [
                              "pt",
                              "en"
                            ]
                          },
                          "subject": {
                            "type": "string",
                            "maxLength": 200,
                            "description": "Email only, required there."
                          },
                          "body": {
                            "type": "string",
                            "description": "At most 250 characters for an SMS, 2000 for an email. Placeholders: {biller}, {number}, {amount}, {due_date}, {account} (masked)."
                          }
                        }
                      },
                      "description": "The biller's own wording."
                    },
                    "defaults": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "kind",
                          "channel",
                          "language",
                          "body"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "invoice_new",
                              "invoice_reminder",
                              "invoice_paid"
                            ]
                          },
                          "channel": {
                            "type": "string",
                            "enum": [
                              "sms",
                              "email"
                            ]
                          },
                          "language": {
                            "type": "string",
                            "enum": [
                              "pt",
                              "en"
                            ]
                          },
                          "subject": {
                            "type": "string",
                            "maxLength": 200,
                            "description": "Email only, required there."
                          },
                          "body": {
                            "type": "string",
                            "description": "At most 250 characters for an SMS, 2000 for an email. Placeholders: {biller}, {number}, {amount}, {due_date}, {account} (masked)."
                          }
                        }
                      },
                      "description": "KulPay's wording, used for what the biller does not word."
                    },
                    "daily_cap": {
                      "type": "integer",
                      "description": "Notices one payer receives a day at most."
                    },
                    "quiet_hours": {
                      "type": "string",
                      "example": "21:00-08:00 Africa/Maputo"
                    }
                  }
                },
                "example": {
                  "notify_payers": false,
                  "templates": [],
                  "defaults": [
                    {
                      "kind": "invoice_new",
                      "channel": "sms",
                      "language": "pt",
                      "body": "{biller}: nova fatura {number} de {amount}, a pagar at\u00e9 {due_date}."
                    },
                    {
                      "kind": "invoice_new",
                      "channel": "sms",
                      "language": "en",
                      "body": "{biller}: new invoice {number} for {amount}, due {due_date}."
                    }
                  ],
                  "daily_cap": 3,
                  "quiet_hours": "21:00-08:00 Africa/Maputo"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putNoticeSettings",
        "summary": "Switch invoice notices on or off, and word them",
        "description": "Replaces the whole set: `notify_payers` (required) and `templates`, at most one per kind, channel and language; what you do not word uses KulPay's (`defaults`). KulPay adds the STOP line itself. Records `biller.notices_updated`. KulPay can send the payers you enrolled with their consent (lawful_basis consent, consent recorded) their invoice notices itself (F3-15): a new invoice, the reminder three days before it is due, and the payment confirmation \u2014 by SMS when the enrolment has a phone number, by email otherwise, in the payer's language. Never in the quiet hours (a notice written from 21:00 to 08:00 Maputo goes out at 08:00), and at most `daily_cap` a day per payer. Every SMS ends with how to reply STOP and every email with a link that stops them; a stop is recorded (`bill_payer.notices_stopped`) and shown on GET /v1/biller/payers. Payers enrolled on any other basis receive nothing. Channels keep receiving their webhooks either way. Requires `payers:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "notify_payers"
                ],
                "properties": {
                  "notify_payers": {
                    "type": "boolean"
                  },
                  "templates": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "kind",
                        "channel",
                        "language",
                        "body"
                      ],
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "invoice_new",
                            "invoice_reminder",
                            "invoice_paid"
                          ]
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "sms",
                            "email"
                          ]
                        },
                        "language": {
                          "type": "string",
                          "enum": [
                            "pt",
                            "en"
                          ]
                        },
                        "subject": {
                          "type": "string",
                          "maxLength": 200,
                          "description": "Email only, required there."
                        },
                        "body": {
                          "type": "string",
                          "description": "At most 250 characters for an SMS, 2000 for an email. Placeholders: {biller}, {number}, {amount}, {due_date}, {account} (masked)."
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "notify_payers": true,
                "templates": [
                  {
                    "kind": "invoice_new",
                    "channel": "sms",
                    "language": "pt",
                    "body": "{biller}: fatura {number} de {amount}, pague at\u00e9 {due_date}."
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The settings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notify_payers": {
                      "type": "boolean"
                    },
                    "templates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "kind",
                          "channel",
                          "language",
                          "body"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "invoice_new",
                              "invoice_reminder",
                              "invoice_paid"
                            ]
                          },
                          "channel": {
                            "type": "string",
                            "enum": [
                              "sms",
                              "email"
                            ]
                          },
                          "language": {
                            "type": "string",
                            "enum": [
                              "pt",
                              "en"
                            ]
                          },
                          "subject": {
                            "type": "string",
                            "maxLength": 200,
                            "description": "Email only, required there."
                          },
                          "body": {
                            "type": "string",
                            "description": "At most 250 characters for an SMS, 2000 for an email. Placeholders: {biller}, {number}, {amount}, {due_date}, {account} (masked)."
                          }
                        }
                      },
                      "description": "The biller's own wording."
                    },
                    "defaults": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "kind",
                          "channel",
                          "language",
                          "body"
                        ],
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "invoice_new",
                              "invoice_reminder",
                              "invoice_paid"
                            ]
                          },
                          "channel": {
                            "type": "string",
                            "enum": [
                              "sms",
                              "email"
                            ]
                          },
                          "language": {
                            "type": "string",
                            "enum": [
                              "pt",
                              "en"
                            ]
                          },
                          "subject": {
                            "type": "string",
                            "maxLength": 200,
                            "description": "Email only, required there."
                          },
                          "body": {
                            "type": "string",
                            "description": "At most 250 characters for an SMS, 2000 for an email. Placeholders: {biller}, {number}, {amount}, {due_date}, {account} (masked)."
                          }
                        }
                      },
                      "description": "KulPay's wording, used for what the biller does not word."
                    },
                    "daily_cap": {
                      "type": "integer",
                      "description": "Notices one payer receives a day at most."
                    },
                    "quiet_hours": {
                      "type": "string",
                      "example": "21:00-08:00 Africa/Maputo"
                    }
                  }
                },
                "example": {
                  "notify_payers": true,
                  "templates": [],
                  "defaults": [
                    {
                      "kind": "invoice_new",
                      "channel": "sms",
                      "language": "pt",
                      "body": "{biller}: nova fatura {number} de {amount}, a pagar at\u00e9 {due_date}."
                    },
                    {
                      "kind": "invoice_new",
                      "channel": "sms",
                      "language": "en",
                      "body": "{biller}: new invoice {number} for {amount}, due {due_date}."
                    }
                  ],
                  "daily_cap": 3,
                  "quiet_hours": "21:00-08:00 Africa/Maputo"
                }
              }
            }
          },
          "400": {
            "description": "A template is not valid (kind, channel, language, length or an unknown placeholder).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "invalid_request",
                  "detail": "notify_payers is required.",
                  "error": {
                    "code": "invalid_request",
                    "message": "notify_payers is required."
                  },
                  "status": 400,
                  "title": "Invalid request",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      }
    },
    "/sms/inbound": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "smsInbound",
        "summary": "The SMS provider forwards payers' replies",
        "description": "For the SMS provider, not for partners (F3-15): a payer's reply to an invoice notice, `{from, text}` as JSON or a form, authenticated by the shared token configured as KULPAY_BILLS_SMS_INBOUND_TOKEN (Authorization: Bearer). STOP (or PARAR, PARE, SAIR, CANCELAR) stops the notices of every enrolment with that number at once and withdraws those still waiting; any other reply is ignored. Without a configured token every call is refused.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "from",
                  "text"
                ],
                "properties": {
                  "from": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "from": "+258841234567",
                "text": "STOP"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{stopped}`: how many enrolments stopped.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stopped": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "stopped": 1
                }
              }
            }
          },
          "401": {
            "description": "The token is missing or wrong.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "unauthorized",
                  "detail": "The SMS provider's token is missing or wrong.",
                  "error": {
                    "code": "unauthorized",
                    "message": "The SMS provider's token is missing or wrong."
                  },
                  "status": 401,
                  "title": "Unauthorized",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-payments/{id}/reverse": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "reverseBillPayment",
        "summary": "Reverse a succeeded bill payment",
        "description": "Reverses a succeeded agent-model payment in full (F3-16): its money did not reach the biller after all, the channel refunded its customer, or the biller reports it never received it. One operator asks with a reason; a second approves (`bill.reverse`). The reversal undoes the ledger (the biller's payable, KulPay's fee and the channel's commission) and gives the total back to the channel's balance; a payment collected from the payer first is refunded to the payer (`bill_payment.refunded`, status `reversed`). The channel receives `bill_payment.reversed`. Its invoice opens again (paid to open, or partially_paid while other payments remain) and the biller receives `bill_invoice.reopened` with the reason; when an earlier remittance already paid the biller that payment, the next remittance deducts it (`adjustment_minor`). Principal-model sales (prepaid tokens, vouchers) are final with their supplier and are refused. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              },
              "example": {
                "reason": "The biller's bank returned the credit"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KUYO4KZt1AMD7NPEGgTx2VrIB7",
                    "kind": "bill.resolve",
                    "subject_id": "3KUYJ7FUo4vHy1IKzBbOs5QmDw2",
                    "tenant_id": "3KUXV2rGa7hTj4uWkMnAe8CyPi8",
                    "payload": "{\"outcome\":\"failed\"}",
                    "reason": "EDM confirmed by phone that no token was issued for this request",
                    "prepared_by": "8f3c2a7e5b1d4c6a9e0f2b7d4a1c5e83",
                    "status": "pending",
                    "created_at": "2026-10-11T10:02:37Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "No reason, not succeeded, or a principal-model sale.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "invalid_request",
                  "detail": "a reason of at most 500 characters is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "a reason of at most 500 characters is required"
                  },
                  "status": 400,
                  "title": "Bad Request",
                  "type": "about:blank"
                }
              }
            }
          },
          "404": {
            "description": "No such bill payment.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  },
                  "status": 404,
                  "title": "Not Found",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/bill-channels/{id}/access": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getChannelAccess",
        "summary": "A channel's access to full names",
        "description": "A bill lookup answers the customer's name masked. A channel whose contract allows it may see the full name (F3-17): an operator switches it on with the contract's reference and a second approves (`bill.channel_unmask`, `bill_channel.unmasking_changed`). Every lookup that reveals a name is recorded in the audit trail as `bill_lookup.name_revealed`, the name masked there. Switching it off takes effect once approved. Requires `admin:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The channel (organisation) id."
          }
        ],
        "responses": {
          "200": {
            "description": "The access; the default (masked) when never set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel_id": {
                      "type": "string"
                    },
                    "unmasked_names": {
                      "type": "boolean",
                      "description": "The channel sees customers' full names in bill lookups."
                    },
                    "contract_reference": {
                      "type": "string",
                      "description": "The contract that allows it."
                    },
                    "approval_id": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "channel_id": "3Kb2fN21UgrE7TzaEzY5HSocv5M",
                  "unmasked_names": true,
                  "contract_reference": "CTR-2026-014",
                  "approval_id": "3Kb2fIkT33EXgUXHykp4aEZ0CXf",
                  "updated_at": "2026-10-12T13:34:09.017827097Z"
                }
              }
            }
          },
          "404": {
            "description": "No such channel.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "not_found",
                  "detail": "no such channel",
                  "error": {
                    "code": "not_found",
                    "message": "no such channel"
                  },
                  "status": 404,
                  "title": "Not Found",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putChannelAccess",
        "summary": "Let a channel see full names, or stop it",
        "description": "A bill lookup answers the customer's name masked. A channel whose contract allows it may see the full name (F3-17): an operator switches it on with the contract's reference and a second approves (`bill.channel_unmask`, `bill_channel.unmasking_changed`). Every lookup that reveals a name is recorded in the audit trail as `bill_lookup.name_revealed`, the name masked there. Switching it off takes effect once approved. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "unmasked_names",
                  "reason"
                ],
                "properties": {
                  "unmasked_names": {
                    "type": "boolean"
                  },
                  "contract_reference": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Required to switch it on."
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "unmasked_names": true,
                "contract_reference": "CTR-2026-014",
                "reason": "Contract clause 7.2 allows full names"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "`{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KUYO4KZt1AMD7NPEGgTx2VrIB7",
                    "kind": "bill.resolve",
                    "subject_id": "3KUYJ7FUo4vHy1IKzBbOs5QmDw2",
                    "tenant_id": "3KUXV2rGa7hTj4uWkMnAe8CyPi8",
                    "payload": "{\"outcome\":\"failed\"}",
                    "reason": "EDM confirmed by phone that no token was issued for this request",
                    "prepared_by": "8f3c2a7e5b1d4c6a9e0f2b7d4a1c5e83",
                    "status": "pending",
                    "created_at": "2026-10-11T10:02:37Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "unmasked_names missing, or switched on without a contract_reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "invalid_request",
                  "detail": "unmasked_names is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "unmasked_names is required"
                  },
                  "status": 400,
                  "title": "Bad Request",
                  "type": "about:blank"
                }
              }
            }
          },
          "404": {
            "description": "No such channel.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "not_found",
                  "detail": "no such channel",
                  "error": {
                    "code": "not_found",
                    "message": "no such channel"
                  },
                  "status": 404,
                  "title": "Not Found",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}/schema-versions": {
      "put": {
        "tags": [
          "Events"
        ],
        "operationId": "setWebhookSchemaVersions",
        "summary": "Pin event types to schema versions",
        "description": "Events are recorded at the latest schema version of their type (each envelope carries `schema_version`). Pin an event type to a version and this endpoint receives it in that version, converted by KulPay, for as long as the version is supported; pinning the current version shields you from the next one. An event the pinned version cannot express (a status it did not have) arrives in the current version, its envelope saying which. When a version is scheduled to retire, the endpoint's contacts are emailed 90 days before (`webhook_endpoint.schema_retiring`, stage notice) and from the date the type arrives in its current version (stage retired). GET /v1/webhook-schema-versions lists the versions, which can be pinned and when they retire. Replaces the endpoint's pins; `{}` unpins every type. Records `webhook_endpoint.schema_pinned`. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "schema_versions"
                ],
                "properties": {
                  "schema_versions": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "description": "Event types pinned to a schema version (F3-18).",
                    "example": {
                      "bill_invoice.paid": 1
                    }
                  }
                }
              },
              "example": {
                "schema_versions": {
                  "bill_invoice.paid": 1,
                  "payment.succeeded": 2
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "paused",
                        "disabled"
                      ]
                    },
                    "disabled_reason": {
                      "enum": [
                        "paused",
                        "gone",
                        "dead_letters"
                      ]
                    },
                    "event_types": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "mode": "test",
                  "url": "https://merchant.example.co.mz/kulpay/return",
                  "enabled": true,
                  "status": "active",
                  "disabled_reason": "paused",
                  "event_types": [
                    "payment.succeeded"
                  ],
                  "contacts": [
                    "string"
                  ],
                  "created_at": "2026-10-10T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/webhook-schema-versions": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listWebhookSchemaVersions",
        "summary": "Webhook schema versions",
        "description": "Events are recorded at the latest schema version of their type (each envelope carries `schema_version`). Pin an event type to a version and this endpoint receives it in that version, converted by KulPay, for as long as the version is supported; pinning the current version shields you from the next one. An event the pinned version cannot express (a status it did not have) arrives in the current version, its envelope saying which. When a version is scheduled to retire, the endpoint's contacts are emailed 90 days before (`webhook_endpoint.schema_retiring`, stage notice) and from the date the type arrives in its current version (stage retired). GET /v1/webhook-schema-versions lists the versions, which can be pinned and when they retire. Requires `webhooks:manage` or `admin:read`.",
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "event_type": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "current": {
                            "type": "boolean"
                          },
                          "pinnable": {
                            "type": "boolean",
                            "description": "KulPay can deliver it and it is not retired."
                          },
                          "retires_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "event_type": "payment.succeeded",
                      "version": 1,
                      "current": true,
                      "pinnable": true,
                      "retires_at": "2026-10-10T09:30:00Z"
                    }
                  ],
                  "total": 12
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/engagement-digest": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getEngagementDigest",
        "summary": "Your daily engagement digest",
        "description": "One webhook a day instead of one per engagement event (F3-18): with the digest on, `bill_invoice.sent`, `bill_invoice.reminder_sent`, `bill_invoice.opened`, `bill_invoice.pinned` and `bill_invoice.unpinned` are still recorded (the invoice timeline and the events API show them) but not sent as webhooks; `biller.engagement_digest` arrives the morning after with how many of your invoices were sent, reminded, opened, pinned and paid that day (Africa/Maputo). Payment and lifecycle events keep coming one by one. Requires `invoices:read`.",
        "responses": {
          "200": {
            "description": "`{enabled}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "enabled": true
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "putEngagementDigest",
        "summary": "Switch the daily engagement digest on or off",
        "description": "One webhook a day instead of one per engagement event (F3-18): with the digest on, `bill_invoice.sent`, `bill_invoice.reminder_sent`, `bill_invoice.opened`, `bill_invoice.pinned` and `bill_invoice.unpinned` are still recorded (the invoice timeline and the events API show them) but not sent as webhooks; `biller.engagement_digest` arrives the morning after with how many of your invoices were sent, reminded, opened, pinned and paid that day (Africa/Maputo). Payment and lifecycle events keep coming one by one. Requires `invoices:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{enabled}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/admin/routing/lookup-preview": {
      "post": {
        "tags": [
          "Routing"
        ],
        "operationId": "previewLookupRouting",
        "summary": "Preview where deposit-reference lookups go",
        "description": "Routes sample deposit references through the active rule set and through a proposed one (IO-1.1), without calling any resolver or recording anything: for each, the rule and the resolver now and proposed, and whether it changes. Use it before proposing a rule set with reference conditions. Requires `connectors:read`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rules",
                  "references"
                ],
                "properties": {
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "The proposed rules, as in POST /v1/admin/routing/versions."
                  },
                  "references": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 200
                  },
                  "account_id": {
                    "type": "string",
                    "description": "The partner whose lookups are previewed."
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "test",
                      "live"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "default": "cash_deposit"
                  }
                }
              },
              "example": {
                "rules": [
                  {
                    "id": "contadores",
                    "match": {
                      "types": [
                        "NAME_LOOKUP"
                      ],
                      "reference": {
                        "digits_only": true,
                        "min_length": 11,
                        "max_length": 11,
                        "prefixes": [
                          "0"
                        ]
                      }
                    },
                    "targets": [
                      {
                        "psp_id": "psp_resolver",
                        "weight": 1
                      }
                    ]
                  }
                ],
                "references": [
                  "01234567890",
                  "841234567"
                ],
                "mode": "test"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "reference": {
                            "type": "string"
                          },
                          "normalized": {
                            "type": "string"
                          },
                          "current": {
                            "type": "object",
                            "properties": {
                              "rule_id": {
                                "type": "string"
                              },
                              "psp_id": {
                                "type": "string"
                              },
                              "rejected": {
                                "type": "string"
                              }
                            }
                          },
                          "proposed": {
                            "type": "object",
                            "properties": {
                              "rule_id": {
                                "type": "string"
                              },
                              "psp_id": {
                                "type": "string"
                              },
                              "rejected": {
                                "type": "string"
                              }
                            }
                          },
                          "changed": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "reference": "01234567890",
                      "normalized": "01234567890",
                      "current": {
                        "rule_id": "method_binding",
                        "psp_id": "psp_kuloffice"
                      },
                      "proposed": {
                        "rule_id": "contadores",
                        "psp_id": "psp_resolver"
                      },
                      "changed": true
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/admin/deposit-credits": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listDepositCredits",
        "summary": "List deposit credits at the resolver",
        "description": "Confirmed deposits applied to the wallet their resolver decided (IO-2.4), newest first; `status` filters (`review_required` is the queue to resolve). Requires `reconciliation:read`. Each step is an event: `cash_in.application_pending`, `cash_in.application_retry_scheduled`, `cash_in.application_review_required`, and the outcomes, sent as webhooks to the partner the cash was deposited with: `cash_in.application_applied` (with the resolver's `transaction_id`) and `cash_in.application_rejected` (with its `reason`).",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "unknown",
                "applied",
                "rejected",
                "review_required"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The credits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "deposit_id": {
                            "type": "string"
                          },
                          "intent_id": {
                            "type": "string"
                          },
                          "lookup_id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "destination_kind": {
                            "type": "string",
                            "enum": [
                              "wallet"
                            ]
                          },
                          "amount_minor": {
                            "type": "integer"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "value_date": {
                            "type": "string",
                            "format": "date"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "unknown",
                              "applied",
                              "rejected",
                              "review_required"
                            ]
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "window_until": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "transaction_id": {
                            "type": "string",
                            "description": "The resolver's transaction."
                          },
                          "reason": {
                            "type": "string"
                          },
                          "last_error": {
                            "type": "string",
                            "enum": [
                              "timeout",
                              "no_answer",
                              "refused"
                            ]
                          },
                          "decided_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KcR1pQm8sZtV2wXyA4bC6dE7fG",
                      "deposit_id": "3KAxj9xanJOoXMxIiKgfRm2KJkz",
                      "intent_id": "3KbQ2rLs9tYuW3xZaB5cD7eF8gH",
                      "lookup_id": "3KbP1qKr8sXtV2wYzA4bC6dE7fG",
                      "psp_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "destination_kind": "wallet",
                      "amount_minor": 50000,
                      "currency": "MZN",
                      "value_date": "2026-10-08",
                      "status": "review_required",
                      "attempts": 14,
                      "window_until": "2026-10-09T10:15:00Z",
                      "reason": "window_expired",
                      "last_error": "timeout",
                      "created_at": "2026-10-08T10:15:00Z",
                      "updated_at": "2026-10-09T10:15:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/deposit-credits/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "getDepositCredit",
        "summary": "Get a deposit credit",
        "description": "A credit and where it stands: pending (to send), unknown (sent without an answer: queried before it is sent again), applied, rejected or review_required. Requires `reconciliation:read`. Each credit has its own `id`, the resolver's idempotency key; a deposit assigned to another wallet after a rejection gets a new credit.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The credit.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deposit_id": {
                      "type": "string"
                    },
                    "intent_id": {
                      "type": "string"
                    },
                    "lookup_id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "destination_kind": {
                      "type": "string",
                      "enum": [
                        "wallet"
                      ]
                    },
                    "amount_minor": {
                      "type": "integer"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "value_date": {
                      "type": "string",
                      "format": "date"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "unknown",
                        "applied",
                        "rejected",
                        "review_required"
                      ]
                    },
                    "attempts": {
                      "type": "integer"
                    },
                    "next_attempt_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "window_until": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "transaction_id": {
                      "type": "string",
                      "description": "The resolver's transaction."
                    },
                    "reason": {
                      "type": "string"
                    },
                    "last_error": {
                      "type": "string",
                      "enum": [
                        "timeout",
                        "no_answer",
                        "refused"
                      ]
                    },
                    "decided_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KcR1pQm8sZtV2wXyA4bC6dE7fG",
                  "deposit_id": "3KAxj9xanJOoXMxIiKgfRm2KJkz",
                  "intent_id": "3KbQ2rLs9tYuW3xZaB5cD7eF8gH",
                  "lookup_id": "3KbP1qKr8sXtV2wYzA4bC6dE7fG",
                  "psp_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "destination_kind": "wallet",
                  "amount_minor": 50000,
                  "currency": "MZN",
                  "value_date": "2026-10-08",
                  "status": "review_required",
                  "attempts": 14,
                  "window_until": "2026-10-09T10:15:00Z",
                  "reason": "window_expired",
                  "last_error": "timeout",
                  "created_at": "2026-10-08T10:15:00Z",
                  "updated_at": "2026-10-09T10:15:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such deposit credit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such deposit credit",
                  "error": {
                    "code": "not_found",
                    "message": "no such deposit credit"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/deposit-credits/{id}/resolve": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "resolveDepositCredit",
        "summary": "Resolve a deposit credit in review",
        "description": "`action` applied (with the resolver's `transaction_id`), rejected, or retry (queried, then sent again, for another 24 h), with a reason; a second person approves (`POST /v1/admin/approvals/{id}/decide`). Only a credit in `review_required`. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action",
                  "reason"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "applied",
                      "rejected",
                      "retry"
                    ]
                  },
                  "transaction_id": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "applied",
                "transaction_id": "txn_blnk_7f3a9c",
                "reason": "KulOffice shows the credit posted in BLNK; its answer timed out"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "cash_in.application.resolve",
                    "subject_id": "3KcR1pQm8sZtV2wXyA4bC6dE7fG",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "payload": "{\"action\":\"applied\",\"transaction_id\":\"txn_blnk_7f3a9c\"}",
                    "reason": "KulOffice shows the credit posted in BLNK; its answer timed out",
                    "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "status": "pending",
                    "created_at": "2026-10-09T15:12:40Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not in review, or an action without what it needs.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "only a credit in review can be resolved",
                  "error": {
                    "code": "invalid_request",
                    "message": "only a credit in review can be resolved"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such deposit credit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such deposit credit",
                  "error": {
                    "code": "not_found",
                    "message": "no such deposit credit"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/deposit-credits/{id}/reverse": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "reverseDepositCredit",
        "summary": "Take an applied credit back",
        "description": "The bank returned a deposit already credited to a KulOffice wallet (IO-2.7): prepares the credit's reversal for a second operator (`cash_in.credit_reversal_requested` once approved). KulPay then asks the resolver to take the credit back, keyed by the credit, until it answers: it debits the customer or, short of balance, records the debt and holds the wallet for withdrawals. Reversed (`cash_in.credit_reversed`), the deposit is returned: the account it credited gives the money back, and the partner hears the outcome `returned`. A refusal, or no answer within 72 hours, raises a `credit_reversal_failed` exception. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "the bank returned the deposit (cheque bounced)"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string"
                        },
                        "subject_id": {
                          "type": "string"
                        },
                        "tenant_id": {
                          "type": "string"
                        },
                        "payload": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "prepared_by": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "cash_in.application.resolve",
                    "subject_id": "3KcR1pQm8sZtV2wXyA4bC6dE7fG",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "payload": "{\"action\":\"applied\",\"transaction_id\":\"txn_blnk_7f3a9c\"}",
                    "reason": "KulOffice shows the credit posted in BLNK; its answer timed out",
                    "prepared_by": "3KbJcVvQe4i8rdAAmWa6EA0bvC2",
                    "status": "pending",
                    "created_at": "2026-10-09T15:12:40Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not in review, or an action without what it needs.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "only a credit in review can be resolved",
                  "error": {
                    "code": "invalid_request",
                    "message": "only a credit in review can be resolved"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such deposit credit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such deposit credit",
                  "error": {
                    "code": "not_found",
                    "message": "no such deposit credit"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-invoices/lookups": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "lookupBillInvoice",
        "summary": "Find a bill by its biller and an identifier",
        "description": "A channel whose customer holds a bill finds it by the biller code and any identifier the customer has (letters, digits, spaces, \"/\", \"-\", any character), wherever the bill lives (IO-5.4). KulPay's records first: an invoice the biller presented (push, file), by its number as written, the same without spaces or case, or its KulPay ID \u2192 `source: presented`, quote it with `invoice.id` as `invoice_id`. Then the biller, the issuer: a lookup biller is asked now through its supply connector with the identifier as the reference \u2192 `source: lookup`, quote it with `lookup_id` and `product`. Only a bill the channel may pay is found (the biller active, a product open to the channel, something to pay, the identifier not ambiguous); anything else is 404, and a biller that does not answer is 503 biller_unavailable. Requires `bills:read`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "biller",
                  "identifier"
                ],
                "properties": {
                  "biller": {
                    "type": "string"
                  },
                  "identifier": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "The invoice number as the payer holds it, or its KulPay ID."
                  }
                }
              },
              "example": {
                "biller": "AGUAS",
                "identifier": "FT 2026/00123"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The invoice.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "source": {
                      "type": "string"
                    },
                    "biller": {
                      "type": "string"
                    },
                    "biller_name": {
                      "type": "string"
                    },
                    "account_reference": {
                      "type": "string"
                    },
                    "alias": {
                      "type": "string"
                    },
                    "invoice": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "biller": {
                          "type": "string"
                        },
                        "biller_name": {
                          "type": "string"
                        },
                        "number": {
                          "type": "string"
                        },
                        "account_reference": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "string"
                        },
                        "paid": {
                          "type": "string"
                        },
                        "open": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "issue_date": {
                          "type": "string"
                        },
                        "due_date": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "source": "presented",
                  "biller": "AGUAS",
                  "biller_name": "\u00c1guas da Regi\u00e3o de Maputo",
                  "account_reference": "CL-200",
                  "amount_due": "300.00",
                  "currency": "MZN",
                  "due_date": "2026-12-31T00:00:00Z",
                  "invoice": {
                    "id": "3KbQ2rLs9tYuW3xZaB5cD7eF8gH",
                    "biller": "AGUAS",
                    "number": "FT 2026/00123",
                    "account_reference": "CL-200",
                    "amount": "300.00",
                    "paid": "0.00",
                    "open": "300.00",
                    "currency": "MZN",
                    "due_date": "2026-12-31",
                    "status": "open"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not yours or unknown.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          },
          "503": {
            "description": "`biller_unavailable`: the supplier is down or not answering. It passes: retry after `Retry-After` seconds.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Service Unavailable",
                  "status": 503,
                  "code": "biller_unavailable",
                  "detail": "the supplier is not answering; try again shortly",
                  "error": {
                    "code": "biller_unavailable",
                    "message": "the supplier is not answering; try again shortly"
                  }
                }
              }
            },
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            }
          }
        }
      }
    },
    "/callbacks/{psp_id}/{binding_id}/events": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "receivePSPEvent",
        "summary": "Receive an event a PSP notifies KulPay of",
        "description": "A PSP that notifies KulPay of its own changes (KulOffice's outbox, IO-0.5) posts each event here, signed under its binding (kulpay_cms: KulOffice-Signature t=<unix>,v1=<hex HMAC-SHA256 of \"<t>.<body>\">, at most 5 minutes old). Each event_id is processed once: a repeat is 200, nothing done. An aggregate's events apply in their sequence: one that comes early is held and KulPay reads the missing ones from the PSP (GET /v1/events?since=). An event that cannot be processed is dead (psp_event.dead_lettered) with its reason. A delivery that does not verify is 401 and a rejected callback (GET /v1/admin/callbacks).",
        "security": [],
        "parameters": [
          {
            "name": "psp_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The PSP's id, or the account's."
          },
          {
            "name": "binding_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "event_id",
                  "type",
                  "aggregate_type",
                  "aggregate_id",
                  "sequence",
                  "data"
                ],
                "properties": {
                  "event_id": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string"
                  },
                  "schema_version": {
                    "type": "integer"
                  },
                  "aggregate_type": {
                    "type": "string"
                  },
                  "aggregate_id": {
                    "type": "string"
                  },
                  "sequence": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "occurred_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "event_id": "evt_2NbQ1x",
                "type": "wallet.credited",
                "schema_version": 1,
                "aggregate_type": "credit",
                "aggregate_id": "cr_3KhN7okcrh0RiJWq5q1gbfGJqKX",
                "sequence": 1,
                "occurred_at": "2026-10-15T10:00:00Z",
                "data": {
                  "credit_id": "cr_3KhN7okcrh0RiJWq5q1gbfGJqKX",
                  "deposit_id": "3KhN7iNFfIs5YvAwTxaDRGyEpIN"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Taken (or a repeat): its status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "received": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "applied",
                        "held",
                        "dead"
                      ]
                    }
                  }
                },
                "example": {
                  "received": true,
                  "status": "applied"
                }
              }
            }
          },
          "400": {
            "description": "No body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid body",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "An event body of at most 1 MiB is required.",
                  "error": {
                    "code": "invalid_request",
                    "message": "An event body of at most 1 MiB is required."
                  }
                }
              }
            }
          },
          "401": {
            "description": "Did not verify.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Callback rejected",
                  "status": 401,
                  "code": "callback_rejected",
                  "detail": "The event did not pass this binding's verification.",
                  "error": {
                    "code": "callback_rejected",
                    "message": "The event did not pass this binding's verification."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such binding, or it sends no events.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "No such event endpoint.",
                  "error": {
                    "code": "not_found",
                    "message": "No such event endpoint."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psp-events": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listPSPEvents",
        "summary": "List the events PSPs sent",
        "description": "Newest first; `status=dead` are the dead letters (IO-0.5), `held` those waiting for an earlier event of their aggregate. Requires `admin:read`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "applied",
                "held",
                "dead"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "binding_id": {
                            "type": "string"
                          },
                          "event_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "psp_id": {
                            "type": "string"
                          },
                          "mode": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string"
                          },
                          "schema_version": {
                            "type": "integer"
                          },
                          "aggregate_type": {
                            "type": "string"
                          },
                          "aggregate_id": {
                            "type": "string"
                          },
                          "sequence": {
                            "type": "integer"
                          },
                          "occurred_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "applied",
                              "held",
                              "dead"
                            ]
                          },
                          "reason": {
                            "type": "string"
                          },
                          "received_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "applied_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KhQ0aBcDeFgHiJkLmNoPqRsTuV",
                      "binding_id": "3KhP9zYxWvUtSrQpOnMlKjIhGfE",
                      "event_id": "evt_2NbQ1x",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "type": "wallet.credited",
                      "schema_version": 1,
                      "aggregate_type": "credit",
                      "aggregate_id": "cr_3KhN7okcrh0RiJWq5q1gbfGJqKX",
                      "sequence": 1,
                      "occurred_at": "2026-10-15T10:00:00Z",
                      "status": "applied",
                      "received_at": "2026-10-15T10:00:01Z",
                      "applied_at": "2026-10-15T10:00:01Z",
                      "updated_at": "2026-10-15T10:00:01Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/psp-events/{id}/replay": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "replayPSPEvent",
        "summary": "Replay a dead PSP event",
        "description": "A dead event applied again, in its aggregate's order: applied when it is next, held when an earlier one is missing. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "binding_id": {
                      "type": "string"
                    },
                    "event_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "psp_id": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "schema_version": {
                      "type": "integer"
                    },
                    "aggregate_type": {
                      "type": "string"
                    },
                    "aggregate_id": {
                      "type": "string"
                    },
                    "sequence": {
                      "type": "integer"
                    },
                    "occurred_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "applied",
                        "held",
                        "dead"
                      ]
                    },
                    "reason": {
                      "type": "string"
                    },
                    "received_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "applied_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KhQ0aBcDeFgHiJkLmNoPqRsTuV",
                  "binding_id": "3KhP9zYxWvUtSrQpOnMlKjIhGfE",
                  "event_id": "evt_2NbQ1x",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "type": "wallet.credited",
                  "schema_version": 1,
                  "aggregate_type": "credit",
                  "aggregate_id": "cr_3KhN7okcrh0RiJWq5q1gbfGJqKX",
                  "sequence": 1,
                  "occurred_at": "2026-10-15T10:00:00Z",
                  "status": "applied",
                  "received_at": "2026-10-15T10:00:01Z",
                  "applied_at": "2026-10-15T10:00:01Z",
                  "updated_at": "2026-10-15T10:00:01Z"
                }
              }
            }
          },
          "404": {
            "description": "No such event.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such event",
                  "error": {
                    "code": "not_found",
                    "message": "no such event"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not dead, or a later event of its aggregate was applied.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "only a dead event is replayed",
                  "error": {
                    "code": "invalid_state",
                    "message": "only a dead event is replayed"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/interop/dashboard": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getInteropDashboard",
        "summary": "The interoperability at a glance",
        "description": "For KulPay's operators (IO-7.2), over the last `hours` (24 by default): reference lookups by resolver and routing rule, with their found, ambiguous and not_found counts and rate, average and p95 latency and whether the resolver's lookup circuit is open; the credits whose outcome is unknown or in review, the deposits in suspense, the open interop exceptions and the PSPs' events dead-lettered, each with its age; and the alerts: a resolver's circuit open, more than 1% of the last hour's credits with an unknown outcome, a biller event dead-lettered. The same screen is /admin/interop. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 168,
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dashboard.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "lookups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "psp_id": {
                            "type": "string"
                          },
                          "routing_rule": {
                            "type": "string"
                          },
                          "total": {
                            "type": "integer"
                          },
                          "found": {
                            "type": "integer"
                          },
                          "ambiguous": {
                            "type": "integer"
                          },
                          "not_found": {
                            "type": "integer"
                          },
                          "found_rate": {
                            "type": "number"
                          },
                          "avg_latency_ms": {
                            "type": "integer"
                          },
                          "p95_latency_ms": {
                            "type": "integer"
                          },
                          "circuit_open": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "credits": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "age": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "suspended": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "age": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "exceptions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "age": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "dead_events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "amount": {
                            "type": "string"
                          },
                          "since": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "age": {
                            "type": "string"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "alerts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "circuit_open",
                              "unknown_credits",
                              "dead_biller_event"
                            ]
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "unknown_credit_rate_last_hour": {
                      "type": "number"
                    }
                  }
                },
                "example": {
                  "since": "2026-10-14T10:00:00Z",
                  "lookups": [
                    {
                      "psp_id": "3KXgPSPkulOffice00000000001",
                      "routing_rule": "method_binding",
                      "total": 120,
                      "found": 110,
                      "ambiguous": 2,
                      "not_found": 8,
                      "found_rate": 0.9167,
                      "avg_latency_ms": 180,
                      "p95_latency_ms": 420,
                      "circuit_open": false
                    }
                  ],
                  "credits": [],
                  "suspended": [],
                  "exceptions": [],
                  "dead_events": [],
                  "alerts": [],
                  "unknown_credit_rate_last_hour": 0
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque",
        "description": "Bearer token. The sandbox accepts any token when no `public_api_tokens` are configured (default for local dev). When tokens are configured, requests without one are rejected with 401."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional client-supplied UUID. The gateway caches the response for this key for the process lifetime; replays return the cached response.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "ChargeID": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The `provider_payment_id` of the charge."
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request body or query failed validation. The response body lists each field-level issue.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Bad Request",
              "status": 400,
              "code": "invalid_request",
              "detail": "The request body failed validation.",
              "errors": [
                {
                  "field": "money.amount",
                  "message": "must be a positive decimal with at most 2 places"
                }
              ],
              "error": {
                "code": "invalid_request",
                "message": "The request body failed validation.",
                "errors": [
                  {
                    "field": "money.amount",
                    "message": "must be a positive decimal with at most 2 places"
                  }
                ]
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer token.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Unauthorized",
              "status": 401,
              "code": "unauthorized",
              "detail": "Missing or invalid API key.",
              "error": {
                "code": "unauthorized",
                "message": "Missing or invalid API key."
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The named resource was not found.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Not Found",
              "status": 404,
              "code": "not_found",
              "detail": "No payment with this id.",
              "error": {
                "code": "not_found",
                "message": "No payment with this id."
              }
            }
          }
        }
      },
      "InvalidState": {
        "description": "The resource is not in a state that allows this operation (e.g. capturing an already-voided charge).",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Conflict",
              "status": 409,
              "code": "invalid_state",
              "detail": "The payment is already captured.",
              "error": {
                "code": "invalid_state",
                "message": "The payment is already captured."
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "The supplied `Idempotency-Key` was previously used with a different request body.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Conflict",
              "status": 409,
              "code": "idempotency_key_reused",
              "detail": "This Idempotency-Key was used with a different request body.",
              "error": {
                "code": "idempotency_key_reused",
                "message": "This Idempotency-Key was used with a different request body."
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-identity rate limit exceeded. `Retry-After` indicates when to retry.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "string"
            },
            "description": "Seconds to wait before retrying."
          }
        },
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Too Many Requests",
              "status": 429,
              "code": "rate_limited",
              "detail": "Retry after the number of seconds in Retry-After.",
              "error": {
                "code": "rate_limited",
                "message": "Retry after the number of seconds in Retry-After."
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected gateway error.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Internal Server Error",
              "status": 500,
              "code": "internal_error",
              "detail": "An internal error occurred. Retry with the same Idempotency-Key; if it persists, contact support with the X-Request-ID.",
              "error": {
                "code": "internal_error",
                "message": "An internal error occurred. Retry with the same Idempotency-Key; if it persists, contact support with the X-Request-ID."
              }
            }
          }
        }
      },
      "ProblemDetails": {
        "description": "RFC 7807 problem-details response. The `code` field carries one of the enum values in `#/components/schemas/ErrorCode`.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "about:blank",
              "title": "Bad Request",
              "status": 400,
              "code": "invalid_request",
              "detail": "The request could not be processed.",
              "error": {
                "code": "invalid_request",
                "message": "The request could not be processed."
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Money": {
        "type": "object",
        "required": [
          "amount",
          "currency"
        ],
        "properties": {
          "amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]{1,4})?$",
            "description": "Decimal string in major units (e.g. \"19.99\", not 1999). Up to 4 decimal places.",
            "examples": [
              "100.00",
              "19.99"
            ]
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "description": "ISO-4217 alpha-3 currency code.",
            "examples": [
              "MZN",
              "USD",
              "EUR"
            ]
          }
        }
      },
      "Customer": {
        "type": "object",
        "description": "End-user the payment is collected from. All fields optional individually; populate what you have.",
        "properties": {
          "organization_id": {
            "type": "string"
          },
          "user_id": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "tax_id": {
            "type": "string"
          }
        }
      },
      "Recipient": {
        "type": "object",
        "description": "Counterparty for a payout.",
        "properties": {
          "organization_id": {
            "type": "string"
          },
          "user_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "tax_id": {
            "type": "string"
          }
        }
      },
      "MobileWalletDetails": {
        "type": "object",
        "required": [
          "network",
          "phone_number"
        ],
        "properties": {
          "network": {
            "type": "string",
            "examples": [
              "mpesa",
              "emola",
              "mkesh"
            ]
          },
          "phone_number": {
            "type": "string",
            "description": "E.164 number.",
            "examples": [
              "+258840000001"
            ]
          }
        }
      },
      "CardDetails": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "The acquirer's card token or payment method ID (e.g. `pm_\u2026` from Stripe's hosted fields). Mutually exclusive with `saved_payment_id`. KulPay never accepts a card number: one sent here is refused with 400 `gateway.raw_card_data_not_accepted` (PSP-12.2). In test mode the documented test card numbers are accepted and become their test tokens (`tok_test_<brand>_<bin6>_<last4>_<hash>`); a test token cannot pay a live payment."
          },
          "saved_payment_id": {
            "type": "string",
            "description": "Reference to a previously stored card."
          },
          "three_d_secure_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "ReferenceDetails": {
        "type": "object",
        "properties": {
          "network": {
            "type": "string",
            "examples": [
              "simo"
            ]
          },
          "expires_in": {
            "type": "string",
            "description": "Go duration (\"24h\")."
          },
          "entity": {
            "type": "string",
            "description": "Pin a specific entity from the catalog."
          },
          "reference": {
            "type": "string",
            "readOnly": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "readOnly": true
          }
        }
      },
      "BankTransferDetails": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string"
          },
          "account_number": {
            "type": "string"
          },
          "swift_bic": {
            "type": "string"
          },
          "beneficiary_name": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          }
        }
      },
      "ManualDetails": {
        "type": "object",
        "required": [
          "recorded_by",
          "external_reference"
        ],
        "properties": {
          "recorded_by": {
            "type": "string",
            "description": "Operator that recorded the payment."
          },
          "external_reference": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          }
        }
      },
      "MockDetails": {
        "type": "object",
        "description": "Force a specific outcome regardless of fixture matches.",
        "required": [
          "outcome"
        ],
        "properties": {
          "outcome": {
            "type": "string",
            "enum": [
              "succeed",
              "pending",
              "fail"
            ]
          },
          "failure_code": {
            "type": "string"
          },
          "delay": {
            "type": "string",
            "description": "Go duration before resolving."
          }
        }
      },
      "Method": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "Polymorphic method picker. Set `type` to one of the listed values and populate the matching nested object.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "mobile_wallet",
              "card",
              "reference",
              "bank_transfer",
              "manual",
              "mock",
              "custom"
            ]
          },
          "mobile_wallet": {
            "$ref": "#/components/schemas/MobileWalletDetails"
          },
          "card": {
            "$ref": "#/components/schemas/CardDetails"
          },
          "reference": {
            "$ref": "#/components/schemas/ReferenceDetails"
          },
          "bank_transfer": {
            "$ref": "#/components/schemas/BankTransferDetails"
          },
          "manual": {
            "$ref": "#/components/schemas/ManualDetails"
          },
          "mock": {
            "$ref": "#/components/schemas/MockDetails"
          },
          "custom": {
            "type": "object",
            "properties": {
              "extension_type": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "RetryPolicy": {
        "type": "object",
        "description": "Per-charge override for the gateway's outbound webhook retry behaviour. Zero/empty values mean \"no override.\"",
        "properties": {
          "max_attempts": {
            "type": "integer",
            "description": "Total attempts cap. -1 disables retry."
          },
          "initial_delay": {
            "type": "string",
            "description": "Go duration."
          },
          "max_delay": {
            "type": "string",
            "description": "Go duration."
          },
          "backoff_multiplier": {
            "type": "number",
            "format": "double"
          },
          "retry_on": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "examples": [
              [
                "5xx",
                "408",
                "429"
              ]
            ]
          }
        }
      },
      "ChargeRequest": {
        "type": "object",
        "required": [
          "payment_id",
          "money",
          "source"
        ],
        "properties": {
          "payment_id": {
            "type": "string",
            "description": "Caller-assigned identifier. Must be unique per charge; replays of the same value return the original response."
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "source": {
            "$ref": "#/components/schemas/Method"
          },
          "customer": {
            "$ref": "#/components/schemas/Customer"
          },
          "country": {
            "type": "string",
            "description": "ISO-3166 alpha-2. Drives method filtering and default reference entity.",
            "examples": [
              "MZ",
              "PT",
              "AO"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "callback_url": {
            "type": "string",
            "format": "uri"
          },
          "return_url": {
            "type": "string",
            "format": "uri"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Up to 50 key/value pairs (key max 100 chars, value max 500 chars)."
          },
          "open_amount": {
            "type": "boolean",
            "description": "Customer picks the amount at checkout."
          },
          "min_amount": {
            "type": "string"
          },
          "max_amount": {
            "type": "string"
          },
          "suggested_amounts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allowed_sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blocked_sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "retry_policy": {
            "$ref": "#/components/schemas/RetryPolicy"
          }
        }
      },
      "NextAction": {
        "type": "object",
        "description": "What the customer must do before the charge can resolve.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "redirect_to_url",
              "display_otp_form",
              "display_qr_code",
              "display_reference",
              "display_bnpl_redirect",
              "poll",
              "authorize_capture"
            ]
          },
          "redirect_url": {
            "type": "string",
            "format": "uri"
          },
          "return_url": {
            "type": "string",
            "format": "uri"
          },
          "otp_hint": {
            "type": "string"
          },
          "otp_length": {
            "type": "integer"
          },
          "masked_dest": {
            "type": "string"
          },
          "qr_code_url": {
            "type": "string",
            "format": "uri"
          },
          "qr_code_data": {
            "type": "string"
          },
          "entity": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "poll_interval_secs": {
            "type": "integer"
          },
          "authorization_code": {
            "type": "string"
          },
          "auth_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "bnpl_provider": {
            "type": "string"
          }
        }
      },
      "ChargeResult": {
        "type": "object",
        "required": [
          "payment_id",
          "provider_payment_id",
          "status",
          "money",
          "processed_at"
        ],
        "properties": {
          "payment_id": {
            "type": "string"
          },
          "provider_payment_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_action",
              "succeeded",
              "failed",
              "expired",
              "authorized",
              "voided"
            ]
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "source": {
            "$ref": "#/components/schemas/Method"
          },
          "checkout_url": {
            "type": "string",
            "format": "uri"
          },
          "next_action": {
            "$ref": "#/components/schemas/NextAction"
          },
          "authorized_amount": {
            "type": "string"
          },
          "captured_amount": {
            "type": "string"
          },
          "failure_code": {
            "type": "string"
          },
          "failure_message": {
            "type": "string"
          },
          "processed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RefundRequest": {
        "type": "object",
        "required": [
          "refund_id",
          "provider_payment_id"
        ],
        "properties": {
          "refund_id": {
            "type": "string",
            "description": "Caller-assigned identifier; replays return the original response."
          },
          "provider_payment_id": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Money"
          },
          "reason": {
            "type": "string",
            "enum": [
              "duplicate",
              "fraudulent",
              "requested_by_customer",
              "expired_uncaptured",
              "other"
            ]
          }
        }
      },
      "RefundResult": {
        "type": "object",
        "required": [
          "refund_id",
          "provider_refund_id",
          "status",
          "money",
          "processed_at"
        ],
        "properties": {
          "refund_id": {
            "type": "string"
          },
          "provider_refund_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "failure_code": {
            "type": "string"
          },
          "failure_message": {
            "type": "string"
          },
          "processed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PayoutRequest": {
        "type": "object",
        "required": [
          "payout_id",
          "money",
          "destination"
        ],
        "properties": {
          "payout_id": {
            "type": "string"
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "destination": {
            "$ref": "#/components/schemas/Method"
          },
          "recipient": {
            "$ref": "#/components/schemas/Recipient"
          },
          "description": {
            "type": "string"
          },
          "callback_url": {
            "type": "string",
            "format": "uri"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "PayoutResult": {
        "type": "object",
        "required": [
          "payout_id",
          "provider_payout_id",
          "status",
          "money",
          "processed_at"
        ],
        "properties": {
          "payout_id": {
            "type": "string"
          },
          "provider_payout_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "failure_code": {
            "type": "string"
          },
          "failure_message": {
            "type": "string"
          },
          "processed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaymentSnapshot": {
        "type": "object",
        "required": [
          "payment_id",
          "provider_payment_id",
          "status",
          "money",
          "last_updated_at"
        ],
        "properties": {
          "payment_id": {
            "type": "string"
          },
          "provider_payment_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "requires_action",
              "succeeded",
              "failed",
              "expired",
              "authorized",
              "voided"
            ]
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "source": {
            "$ref": "#/components/schemas/Method"
          },
          "checkout_url": {
            "type": "string",
            "format": "uri"
          },
          "next_action": {
            "$ref": "#/components/schemas/NextAction"
          },
          "authorized_amount": {
            "type": "string"
          },
          "captured_amount": {
            "type": "string"
          },
          "failure_code": {
            "type": "string"
          },
          "failure_message": {
            "type": "string"
          },
          "last_updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PayoutSnapshot": {
        "type": "object",
        "required": [
          "payout_id",
          "provider_payout_id",
          "status",
          "money",
          "last_updated_at"
        ],
        "properties": {
          "payout_id": {
            "type": "string"
          },
          "provider_payout_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "succeeded",
              "failed"
            ]
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "destination": {
            "$ref": "#/components/schemas/Method"
          },
          "failure_code": {
            "type": "string"
          },
          "failure_message": {
            "type": "string"
          },
          "last_updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ErrorCode": {
        "type": "string",
        "description": "Stable machine-readable error code: the `code` of an RFC 7807 problem, or of the `error` envelope. Branch on it, never on the message. What each one means, its HTTP status and what to do: /docs/errors. Why an accepted payment failed is its `failure_code`, a separate vocabulary on the same page.",
        "enum": [
          "account_not_found",
          "account_required",
          "account_suspended",
          "already_decided",
          "already_rolled",
          "already_submitted",
          "ambiguous_keys",
          "amount_exceeds_remaining",
          "amount_out_of_range",
          "amount_too_high",
          "amount_too_low",
          "application_incomplete",
          "audit_unavailable",
          "audit_unsigned",
          "beneficiary_name_required",
          "biller_unavailable",
          "callback_rejected",
          "certification_running",
          "client_certificate_required",
          "config_kind_retired",
          "config_required",
          "connectors_disabled",
          "consent_required",
          "csrf_invalid",
          "csrf_missing",
          "customer_not_allowed",
          "deposit_refused",
          "document_tampered",
          "duplicate_originator_reference",
          "endpoint_disabled",
          "fee_bearer_conflict",
          "file_too_large",
          "idempotency_key_required",
          "idempotency_key_reused",
          "idempotency_unavailable",
          "insufficient_balance",
          "insufficient_scope",
          "internal_error",
          "invalid_account",
          "invalid_amount",
          "invalid_api_key",
          "invalid_callback_url",
          "invalid_card",
          "invalid_card_token",
          "invalid_client",
          "invalid_connector",
          "invalid_currency",
          "invalid_expires_at",
          "invalid_idempotency_key",
          "invalid_key",
          "invalid_logo",
          "invalid_method",
          "invalid_mode",
          "invalid_otp",
          "invalid_phone",
          "invalid_phone_pattern",
          "invalid_request",
          "invalid_return_url",
          "invalid_scope",
          "invalid_signature",
          "invalid_state",
          "invalid_transition",
          "invoice_closed",
          "invoice_locked",
          "ip_not_allowed",
          "jobs_unavailable",
          "key_conflict",
          "key_expired",
          "limit_exceeded",
          "live_keys_not_allowed",
          "live_payment",
          "lookup_used",
          "metadata_too_large",
          "metadata_value_too_large",
          "missing_file",
          "missing_name",
          "missing_otp",
          "missing_phone",
          "missing_signature",
          "no_connector_binding",
          "no_route",
          "no_secret_configured",
          "no_url",
          "not_a_biller",
          "not_a_partner",
          "not_found",
          "not_ready",
          "not_receiptable",
          "not_refundable",
          "not_replayable",
          "not_reversible",
          "not_submittable",
          "not_submitted",
          "not_supported",
          "onboarding_unavailable",
          "option_locked",
          "owned_by_origin",
          "pilot_limit",
          "quote_expired",
          "quote_used",
          "range_too_large",
          "rate_limited",
          "raw_card_data_not_accepted",
          "reference_not_found",
          "reference_rejected",
          "request_in_progress",
          "resend_limit",
          "residency_unavailable",
          "risk_declined",
          "same_approver",
          "sample_rejected",
          "sandbox_unavailable",
          "service_unavailable",
          "signature_invalid",
          "signature_timestamp_outside_tolerance",
          "sms_unavailable",
          "stages_unavailable",
          "statement_rejected",
          "stream_capacity",
          "temporarily_unavailable",
          "timeline_unavailable",
          "too_many_attachments",
          "unauthorized",
          "unsupported_content_type",
          "unsupported_currency",
          "unsupported_grant_type",
          "upstream_error",
          "wrong_environment",
          "wrong_region"
        ]
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem document: every error the API returns, with a stable `code` (see /docs/errors).",
        "required": [
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "default": "about:blank"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode"
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "format": "uri"
          },
          "errors": {
            "type": "array",
            "description": "Field-level error list for validation failures.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "error": {
            "type": "object",
            "deprecated": true,
            "description": "The old error envelope ({code, message}, with the same code), sent until its sunset (P-1): once announced, errors carry the Deprecation and Sunset headers, and from that date this member is gone. Read `code`.",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "The signed envelope POSTed to your endpoint: the Event object, restricted to the types below. Verify the signature against the RAW body before trusting any field \u2014 see /docs#webhooks.\n\nOnly `payment.succeeded` means you have been paid. `processing` and `requires_action` are progress reports; acting on them is the most common integration mistake.",
        "required": [
          "id",
          "type",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The event id: the same on every retry and in GET /v1/events/{id}. Deduplicate on it.",
            "example": "2mZp4T0Qx9C1hS8bL5vN3kF7jR2"
          },
          "type": {
            "type": "string",
            "enum": [
              "payment.succeeded",
              "payment.authorized",
              "payment.failed",
              "payment.cancelled",
              "payment.expired",
              "payment.processing",
              "payment.requires_action",
              "payment.disputed",
              "refund.succeeded",
              "refund.failed"
            ],
            "description": "Terminal: succeeded, failed, cancelled, expired, refund.*. Non-terminal: processing, requires_action, authorized (funds held; capture or cancel it), disputed. Payment `data` is the payment v2 object: `status` (created, pending, requires_action, processing, authorized, succeeded, failed, cancelled, expired, disputed, partially_refunded, refunded), `settlement_status` (unsettled, settled, reconciled), `amount_minor`, `provider_code`, `code_family`. The allowed status changes are the lifecycle table on /docs; any other change is refused and recorded as transaction.transition.rejected."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "description": "The payment or refund at the moment the event fired."
          }
        },
        "allOf": [
          {
            "$ref": "#/components/schemas/Event"
          }
        ]
      },
      "EventActor": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "Who or what caused the event.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "api_key",
              "operator",
              "service_account",
              "system",
              "callback",
              "customer",
              "anonymous"
            ]
          },
          "id": {
            "type": "string",
            "description": "Key id, operator subject, `system:<job>` or `callback:<provider>`."
          },
          "ip": {
            "type": "string"
          },
          "user_agent": {
            "type": "string"
          }
        }
      },
      "Event": {
        "type": "object",
        "description": "The event envelope. Webhook bodies are exactly this object. `data` is validated against the type's JSON Schema (see /docs/events) and sensitive values are masked before storage.",
        "required": [
          "id",
          "type",
          "schema_version",
          "aggregate_type",
          "aggregate_id",
          "sequence",
          "occurred_at",
          "recorded_at",
          "created_at",
          "tenant_id",
          "mode",
          "direction",
          "actor",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique event id. Deduplicate on it; it is also the webhook body id and the `since` cursor."
          },
          "type": {
            "type": "string",
            "example": "payment.succeeded"
          },
          "schema_version": {
            "type": "integer",
            "minimum": 1
          },
          "aggregate_type": {
            "type": "string",
            "example": "payment"
          },
          "aggregate_id": {
            "type": "string",
            "description": "The object the event is about."
          },
          "sequence": {
            "type": "integer",
            "minimum": 1,
            "description": "Strictly increasing per aggregate, without gaps. Order events of one object by this, not by arrival."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Same as recorded_at; kept for webhook receivers written against the original envelope."
          },
          "tenant_id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "merchant_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "region": {
            "type": "string",
            "example": "mz"
          },
          "direction": {
            "type": "string",
            "enum": [
              "internal",
              "inbound",
              "outbound"
            ]
          },
          "actor": {
            "$ref": "#/components/schemas/EventActor"
          },
          "correlation_id": {
            "type": "string",
            "description": "The request that caused it (its X-Request-ID)."
          },
          "causation_id": {
            "type": "string",
            "description": "The event that caused it, when one did."
          },
          "idempotency_key": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "description": "Type-specific payload. Schemas: GET /docs/events/{type}/schema.json."
          },
          "prev_hash": {
            "type": "string",
            "description": "Reserved for the tamper-evident audit chain."
          },
          "hash": {
            "type": "string",
            "description": "Reserved for the tamper-evident audit chain."
          },
          "pattern": {
            "type": "string",
            "enum": [
              "outbound_sync",
              "outbound_async",
              "outbound_notification",
              "inbound_request",
              "inbound_callback",
              "inbound_file",
              "outbound_file",
              "status_query"
            ],
            "description": "How the interaction happened (PSP-7.1); absent for internal work."
          }
        }
      },
      "EventList": {
        "type": "object",
        "required": [
          "data",
          "has_more"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Event"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "description": "Pass as `starting_after` (browsing) or `since` (catching up) to continue."
          }
        }
      },
      "DeadLetter": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "consumer": {
            "type": "string"
          },
          "event_id": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "tenant_id": {
            "type": "string"
          },
          "attempts": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed_at": {
            "type": "string",
            "format": "date-time"
          },
          "replayed_by": {
            "type": "string"
          }
        }
      },
      "ProjectionReplay": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "projection": {
            "type": "string",
            "example": "event_stats"
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "Exclusive."
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "completed",
              "failed"
            ]
          },
          "events_read": {
            "type": "integer"
          },
          "rows_written": {
            "type": "integer"
          },
          "checksum": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "started_by": {
            "type": "string"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventPipeline": {
        "type": "object",
        "properties": {
          "bus": {
            "type": "string",
            "enum": [
              "db",
              "nats"
            ]
          },
          "unpublished": {
            "type": "integer",
            "description": "Events recorded but not yet on the bus."
          },
          "oldest_unpublished_age_seconds": {
            "type": "number",
            "description": "Relay lag. An alert is raised past 60 seconds by default."
          },
          "open_dead_letters": {
            "type": "integer"
          },
          "consumers": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "metrics": {
            "type": "object",
            "description": "Published, failed, processed, duplicate, retried and dead-lettered counts; publish lag p99."
          }
        }
      },
      "TimelineItem": {
        "type": "object",
        "required": [
          "id",
          "at",
          "kind",
          "type",
          "outcome",
          "summary"
        ],
        "description": "One record on a timeline. `detail` is masked at the source: event payloads are masked when recorded; requests, callbacks and webhook deliveries contribute metadata only, never bodies or secrets.",
        "properties": {
          "id": {
            "type": "string",
            "description": "Id of the row it was read from (event, audit entry, callback, delivery, ledger entry)."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "enum": [
              "event",
              "request",
              "provider_call",
              "callback",
              "webhook",
              "ledger"
            ],
            "description": "`provider_call` is reserved for the PSP call log (PSP-6.2) and is not emitted yet."
          },
          "type": {
            "type": "string",
            "example": "payment.succeeded"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound",
              "internal"
            ]
          },
          "actor": {
            "type": "string",
            "example": "key:key_123",
            "description": "API key, `operator:<subject>`, `system:<job>` or `callback:<psp>`."
          },
          "outcome": {
            "type": "string",
            "example": "success"
          },
          "duration_ms": {
            "type": "integer",
            "description": "Events: time since the previous stage of the same object. Callbacks and webhooks: time to completion."
          },
          "correlation_id": {
            "type": "string",
            "description": "The request id that caused it. Events and requests with the same value belong together."
          },
          "aggregate_id": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "detail": {
            "type": "object",
            "additionalProperties": true
          },
          "pattern": {
            "type": "string",
            "enum": [
              "outbound_sync",
              "outbound_async",
              "outbound_notification",
              "inbound_request",
              "inbound_callback",
              "inbound_file",
              "outbound_file",
              "status_query"
            ],
            "description": "How the interaction happened (PSP-7.1); absent for internal work."
          }
        }
      },
      "TransactionStage": {
        "type": "object",
        "required": [
          "id",
          "payment_id",
          "stage",
          "outcome",
          "attempt",
          "started_at",
          "ended_at",
          "duration_ms"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "payment_id": {
            "type": "string"
          },
          "stage": {
            "type": "string",
            "enum": [
              "receive",
              "enrich_validate",
              "risk",
              "route",
              "submit",
              "await_confirmation",
              "post",
              "notify",
              "settle",
              "reconcile"
            ]
          },
          "outcome": {
            "type": "string"
          },
          "reason_code": {
            "type": "string"
          },
          "raw_code": {
            "type": "string",
            "description": "The PSP's own code."
          },
          "mapped_code": {
            "type": "string",
            "description": "KulPay's code for it (see the code mappings)."
          },
          "attempt": {
            "type": "integer",
            "minimum": 1
          },
          "psp": {
            "type": "string"
          },
          "transaction_type": {
            "type": "string",
            "example": "SALE"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "ended_at": {
            "type": "string",
            "format": "date-time"
          },
          "duration_ms": {
            "type": "integer"
          },
          "detail": {
            "type": "object",
            "description": "Stage-specific facts (the route chosen, the provider status, the event notified)."
          }
        }
      },
      "TransactionTimeline": {
        "type": "object",
        "required": [
          "transaction_id",
          "account_id",
          "items",
          "counts",
          "truncated"
        ],
        "properties": {
          "transaction_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TimelineItem"
            },
            "description": "Oldest first. At equal times a request precedes what it caused."
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Items per kind."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when a source held more than 500 records for this transaction."
          }
        }
      },
      "AuditChain": {
        "type": "object",
        "required": [
          "chain_id",
          "status"
        ],
        "description": "One hash chain: `tenant:<account>` per tenant, `platform` for platform actions, `http` for the request log.",
        "properties": {
          "chain_id": {
            "type": "string",
            "example": "tenant:acct_123"
          },
          "tenant_id": {
            "type": "string"
          },
          "last_seq": {
            "type": "integer",
            "description": "Chain position of the newest event."
          },
          "last_hash": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "broken",
              "unverified"
            ]
          },
          "verified_seq": {
            "type": "integer",
            "description": "Position up to which the hourly job has verified every hash and link."
          },
          "broken_at_seq": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "last_run_at": {
            "type": "string",
            "format": "date-time"
          },
          "broken_since": {
            "type": "string",
            "format": "date-time"
          },
          "runbook": {
            "type": "string",
            "description": "Set while broken: the runbook to follow."
          }
        }
      },
      "AuditAnchor": {
        "type": "object",
        "required": [
          "id",
          "day",
          "merkle_root",
          "chains",
          "signed",
          "created_at",
          "document"
        ],
        "description": "The day's chain heads, their Merkle root and an Ed25519 signature. Verify `signature` over the compact JSON of `document` with the published key.",
        "properties": {
          "id": {
            "type": "string"
          },
          "day": {
            "type": "string",
            "format": "date"
          },
          "merkle_root": {
            "type": "string"
          },
          "chains": {
            "type": "integer"
          },
          "key_id": {
            "type": "string"
          },
          "signed": {
            "type": "boolean"
          },
          "object_key": {
            "type": "string",
            "description": "Where the anchor is locked in write-once storage (S3 Object Lock, compliance mode). Empty when no anchor bucket is configured."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "document": {
            "type": "object",
            "additionalProperties": true
          },
          "signature": {
            "type": "string",
            "description": "Base64 Ed25519 signature."
          },
          "public_key": {
            "type": "string"
          }
        }
      },
      "Job": {
        "type": "object",
        "required": [
          "job",
          "leader_alive",
          "this_node_leads"
        ],
        "description": "A singleton job. One node per region leads it (a lease renewed every 5 s, lasting 20 s); another takes over within 30 s if the leader dies.",
        "properties": {
          "job": {
            "type": "string",
            "example": "settlement_sync"
          },
          "every": {
            "type": "string",
            "example": "30s"
          },
          "leader_node": {
            "type": "string",
            "description": "The node holding the lease, while it is alive."
          },
          "lease_until": {
            "type": "string",
            "format": "date-time"
          },
          "leader_alive": {
            "type": "boolean"
          },
          "this_node_leads": {
            "type": "boolean"
          },
          "runs_on_this_node": {
            "type": "integer"
          },
          "failures_on_this_node": {
            "type": "integer"
          },
          "last_run_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_success_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_error": {
            "type": "string"
          }
        }
      },
      "ResidencyReport": {
        "type": "object",
        "properties": {
          "region": {
            "type": "string",
            "example": "mz"
          },
          "base_url": {
            "type": "string"
          },
          "data_stores": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "store": {
                  "type": "string"
                },
                "location": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "tenants_by_region": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "tenants_of_other_regions": {
            "type": "integer",
            "description": "Tenants in this database whose region is another: data outside its region. Should be 0 except during an approved migration."
          },
          "other_regions": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "directory_keys": {
            "type": "integer",
            "description": "Keys of other regions' tenants this region redirects."
          },
          "directory_loaded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PSPApplicationData": {
        "type": "object",
        "additionalProperties": false,
        "description": "What a PSP declares. Every field is required to submit; a draft may hold any subset.",
        "properties": {
          "legal_name": {
            "type": "string"
          },
          "registration_number": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Z]{2}$"
          },
          "licences": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "number": {
                  "type": "string"
                },
                "issuer": {
                  "type": "string"
                },
                "expires_on": {
                  "type": "string",
                  "format": "date"
                }
              }
            }
          },
          "roles": {
            "type": "array",
            "items": {
              "enum": [
                "processor",
                "originator",
                "cash_deposit_bank",
                "settlement_partner"
              ]
            }
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "currencies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "transaction_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expected_monthly_volume": {
            "type": "integer"
          },
          "expected_monthly_value": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              }
            }
          },
          "technical_contact": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string"
              }
            }
          },
          "compliance_contact": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "phone": {
                "type": "string"
              }
            }
          },
          "language": {
            "enum": [
              "pt",
              "en"
            ]
          }
        }
      },
      "PSPApplication": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "enum": [
              "draft",
              "submitted"
            ]
          },
          "language": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "missing": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Fields still missing for submission, by name."
          },
          "data": {
            "$ref": "#/components/schemas/PSPApplicationData"
          },
          "draft_token": {
            "type": "string",
            "description": "Returned once, on creation. Send it as KulPay-Application-Token to read, save or submit the draft."
          }
        }
      },
      "PSP": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "application_id": {
            "type": "string"
          },
          "legal_name": {
            "type": "string"
          },
          "registration_number": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "language": {
            "type": "string"
          },
          "status": {
            "enum": [
              "applied",
              "kyb_review",
              "kyb_approved",
              "commercial_setup",
              "technical_setup",
              "certification",
              "pilot",
              "live",
              "info_requested",
              "rejected",
              "suspended",
              "offboarding",
              "offboarded"
            ]
          },
          "kyb_status": {
            "enum": [
              "pending",
              "in_review",
              "info_requested",
              "approved",
              "rejected"
            ]
          },
          "resume_state": {
            "type": "string"
          },
          "risk_tier": {
            "type": "string"
          },
          "state_entered_at": {
            "type": "string",
            "format": "date-time"
          },
          "kyb_approved_at": {
            "type": "string",
            "format": "date-time"
          },
          "live_at": {
            "type": "string",
            "format": "date-time"
          },
          "contract_ref": {
            "type": "string"
          },
          "contract_signed": {
            "type": "boolean"
          },
          "sandbox_enabled_at": {
            "type": "string",
            "format": "date-time"
          },
          "sandbox_key_id": {
            "type": "string"
          },
          "certified_at": {
            "type": "string",
            "format": "date-time"
          },
          "certification_run_id": {
            "type": "string"
          },
          "pilot_started_at": {
            "type": "string",
            "format": "date-time"
          },
          "live_key_id": {
            "type": "string"
          },
          "last_review_at": {
            "type": "string",
            "format": "date-time"
          },
          "review_notice": {
            "enum": [
              "",
              "due",
              "overdue"
            ]
          },
          "emergency_suspended_at": {
            "type": "string",
            "format": "date-time"
          },
          "suspension_review_id": {
            "type": "string"
          },
          "suspension_review_overdue": {
            "type": "boolean"
          }
        }
      },
      "PSPDocument": {
        "type": "object",
        "description": "A KYB document. Stored encrypted; sha256 is the checksum of its content, checked on every read.",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "file_id": {
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "sha256": {
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "accepted",
              "rejected"
            ]
          },
          "review_comment": {
            "type": "string"
          },
          "reviewed_by": {
            "type": "string"
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time"
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PSPScreening": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "kind": {
            "enum": [
              "sanctions",
              "pep"
            ]
          },
          "subject": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "result": {
            "enum": [
              "clear",
              "hit",
              "error",
              "manual_review_required"
            ]
          },
          "reference": {
            "type": "string"
          },
          "notes": {
            "type": "string"
          },
          "evidence_file_id": {
            "type": "string"
          },
          "performed_by": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApprovalRequest": {
        "type": "object",
        "description": "A maker-checker decision: prepared by one person, approved or rejected by a different one.",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "subject_id": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "prepared_by": {
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "approved",
              "rejected"
            ]
          },
          "decided_by": {
            "type": "string"
          },
          "decision_reason": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ConfigVersion": {
        "type": "object",
        "description": "One version of a PSP configuration document. Versions are never edited: a change or a rollback is a new version.",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "kind": {
            "enum": [
              "capability_matrix",
              "orientation_profile",
              "routing",
              "retry_policy",
              "commercial_terms",
              "statement_profile",
              "technical_config"
            ]
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "enum": [
              "pending",
              "active",
              "superseded",
              "rejected"
            ]
          },
          "reason": {
            "type": "string"
          },
          "based_on": {
            "type": "integer",
            "description": "For a rollback, the version it copies."
          },
          "approval_id": {
            "type": "string"
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time"
          },
          "document": {
            "type": "object",
            "description": "The configuration document. commercial_terms follows CommercialTerms."
          }
        }
      },
      "CommercialTerms": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "contract_ref",
          "fee_schedule",
          "funds_flow",
          "limits"
        ],
        "description": "The commercial_terms document (PSP-4.5). Limits may not exceed the defaults of the PSP's risk tier, in its currency.",
        "properties": {
          "contract_ref": {
            "type": "string"
          },
          "signed_copy_file_id": {
            "type": "string"
          },
          "fee_schedule": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "properties": {
                "method": {
                  "type": "string",
                  "description": "A method, or * for any."
                },
                "percent": {
                  "type": "string",
                  "description": "0 to 100."
                },
                "fixed": {
                  "type": "string"
                },
                "currency": {
                  "type": "string"
                }
              }
            }
          },
          "funds_flow": {
            "enum": [
              "collecting",
              "direct"
            ]
          },
          "settlement": {
            "type": "object",
            "description": "Required for collecting.",
            "properties": {
              "frequency_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 31
              },
              "currency": {
                "type": "string"
              },
              "account": {
                "type": "string"
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "per_transaction": {
                "type": "string"
              },
              "daily": {
                "type": "string"
              },
              "monthly": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              }
            }
          }
        }
      },
      "ConfigProposed": {
        "type": "object",
        "properties": {
          "version": {
            "$ref": "#/components/schemas/ConfigVersion"
          },
          "approval": {
            "$ref": "#/components/schemas/ApprovalRequest"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "TechnicalCheck": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "result": {
            "enum": [
              "pass",
              "warn",
              "fail"
            ]
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "TechnicalStep": {
        "type": "object",
        "properties": {
          "step": {
            "enum": [
              "capability_matrix",
              "orientation_profile",
              "endpoints",
              "credentials",
              "webhooks",
              "ip_allowlist",
              "statement_profile"
            ]
          },
          "status": {
            "enum": [
              "pending",
              "passed",
              "warning",
              "failed"
            ]
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TechnicalCheck"
            }
          },
          "config_kind": {
            "type": "string"
          },
          "config_version": {
            "type": "integer"
          },
          "checked_at": {
            "type": "string",
            "format": "date-time"
          },
          "value": {
            "type": "object"
          }
        }
      },
      "TechnicalSetup": {
        "type": "object",
        "description": "Where a PSP's sandbox technical setup stands (PSP-4.6).",
        "properties": {
          "psp_id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "sandbox_enabled": {
            "type": "boolean"
          },
          "sandbox_key_id": {
            "type": "string"
          },
          "sandbox_key_revealable": {
            "type": "boolean",
            "description": "The test key has not yet been shown to the PSP."
          },
          "live_keys_allowed": {
            "type": "boolean",
            "description": "False until pilot."
          },
          "sandbox_methods": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TechnicalStep"
            }
          },
          "complete": {
            "type": "boolean"
          }
        }
      },
      "CertificationRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "status": {
            "enum": [
              "running",
              "passed",
              "failed"
            ]
          },
          "rerun_of": {
            "type": "string"
          },
          "cases": {
            "type": "integer"
          },
          "mandatory": {
            "type": "integer"
          },
          "passed": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "capability_matrix_version": {
            "type": "integer"
          },
          "started_by": {
            "type": "string"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "report_sha256": {
            "type": "string"
          },
          "signature": {
            "type": "string",
            "description": "Base64 Ed25519 signature over the report bytes."
          },
          "signing_key_id": {
            "type": "string"
          }
        }
      },
      "CertificationCase": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "seq": {
            "type": "integer"
          },
          "key": {
            "type": "string",
            "example": "payment/card/decline/fraud"
          },
          "kind": {
            "enum": [
              "outcome",
              "refund",
              "capture",
              "void",
              "payout",
              "idempotency",
              "callback",
              "retry",
              "signature",
              "statement"
            ]
          },
          "title": {
            "type": "string"
          },
          "mandatory": {
            "type": "boolean"
          },
          "status": {
            "enum": [
              "pending",
              "running",
              "passed",
              "failed",
              "skipped"
            ]
          },
          "expected": {
            "type": "string"
          },
          "actual": {
            "type": "string"
          },
          "payment_id": {
            "type": "string",
            "description": "The payment the case created: GET /v1/transactions/{payment_id}/timeline."
          },
          "carried_from": {
            "type": "string",
            "description": "For a rerun, the run where this case passed."
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Gate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "met": {
            "type": "boolean"
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "PilotSettings": {
        "type": "object",
        "description": "What a PSP may do in pilot. Limits never exceed the contracted ones.",
        "properties": {
          "per_transaction": {
            "type": "string"
          },
          "daily": {
            "type": "string"
          },
          "monthly": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "merchants": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allow-list: a live payment in pilot must carry metadata.merchant_id from it."
          },
          "routing_weight": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "description": "% of eligible traffic routed to the PSP."
          }
        }
      },
      "PSPIncident": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string"
          },
          "severity": {
            "enum": [
              "critical",
              "major",
              "minor"
            ]
          },
          "summary": {
            "type": "string"
          },
          "opened_by": {
            "type": "string"
          },
          "opened_at": {
            "type": "string",
            "format": "date-time"
          },
          "resolution": {
            "type": "string"
          },
          "resolved_by": {
            "type": "string"
          },
          "resolved_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GoLive": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "pilot_gates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Gate"
            }
          },
          "live_gates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Gate"
            }
          },
          "pilot_settings": {
            "$ref": "#/components/schemas/PilotSettings"
          },
          "live_key_id": {
            "type": "string"
          },
          "incidents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PSPIncident"
            }
          },
          "go_live_checklist_stored": {
            "type": "boolean"
          }
        }
      },
      "ReviewItem": {
        "type": "object",
        "properties": {
          "psp": {
            "$ref": "#/components/schemas/PSP"
          },
          "kind": {
            "enum": [
              "periodic",
              "emergency_suspension"
            ]
          },
          "due_at": {
            "type": "string",
            "format": "date-time"
          },
          "overdue": {
            "type": "boolean"
          },
          "approval_id": {
            "type": "string",
            "description": "For an emergency suspension: the review to decide."
          }
        }
      },
      "TransactionType": {
        "type": "object",
        "description": "One transaction type of the catalogue (PSP-5.1).",
        "properties": {
          "code": {
            "enum": [
              "SALE",
              "AUTH",
              "CAPTURE",
              "VOID",
              "REFUND",
              "REVERSAL",
              "PAYOUT",
              "CASH_IN",
              "BILL_PAYMENT",
              "NAME_LOOKUP",
              "STATUS_QUERY",
              "DISPUTE",
              "SETTLEMENT"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "moves_money": {
            "type": "boolean",
            "description": "Changes balances."
          },
          "holds_funds": {
            "type": "boolean",
            "description": "Reserves funds without moving them (AUTH)."
          },
          "debit": {
            "enum": [
              "customer",
              "merchant",
              "beneficiary",
              "agent",
              "settlement"
            ]
          },
          "credit": {
            "enum": [
              "customer",
              "merchant",
              "beneficiary",
              "agent",
              "settlement"
            ]
          },
          "amount_required": {
            "type": "boolean"
          },
          "parent": {
            "type": "object",
            "description": "The earlier transaction this one acts on, when required (e.g. CAPTURE needs an authorized AUTH).",
            "properties": {
              "types": {
                "type": "array",
                "items": {
                  "enum": [
                    "SALE",
                    "AUTH",
                    "CAPTURE",
                    "VOID",
                    "REFUND",
                    "REVERSAL",
                    "PAYOUT",
                    "CASH_IN",
                    "BILL_PAYMENT",
                    "TRANSFER",
                    "NAME_LOOKUP",
                    "STATUS_QUERY",
                    "DISPUTE",
                    "MANDATE",
                    "RECURRING",
                    "TOKENIZE",
                    "BALANCE_INQUIRY",
                    "SETTLEMENT",
                    "FX_QUOTE"
                  ]
                }
              },
              "states": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "supported",
              "reserved"
            ],
            "description": "supported: processed end to end. reserved: a name kept for later, listed only with ?include=reserved; no PSP may declare it (P-3)."
          }
        }
      },
      "Currency": {
        "type": "object",
        "description": "An ISO 4217 currency. Amounts are stored as integer minor units: amount x 10^exponent (MZN 125.50 is 12550).",
        "properties": {
          "code": {
            "type": "string"
          },
          "exponent": {
            "type": "integer",
            "example": 2
          },
          "name": {
            "type": "string"
          }
        }
      },
      "CapabilityRow": {
        "type": "object",
        "required": [
          "type"
        ],
        "description": "One enabled transaction type of a PSP (PSP-5.2). Amounts are decimals in each listed currency.",
        "properties": {
          "type": {
            "type": "string",
            "description": "A code of GET /v1/transaction-types, once per matrix."
          },
          "methods": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "direction": {
            "enum": [
              "inbound",
              "outbound",
              "both"
            ],
            "description": "Default by type (PAYOUT outbound, TRANSFER both, else inbound)."
          },
          "mode": {
            "enum": [
              "sync",
              "async"
            ],
            "default": "async"
          },
          "currencies": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ISO 3166 alpha-2; default the PSP's country."
          },
          "min_amount": {
            "type": "string"
          },
          "max_amount": {
            "type": "string"
          },
          "daily_limit": {
            "type": "string"
          },
          "monthly_limit": {
            "type": "string"
          },
          "partial": {
            "type": "boolean",
            "description": "Partial capture or refund allowed."
          },
          "refund_window_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 540
          },
          "three_ds": {
            "type": "boolean"
          },
          "idempotency": {
            "type": "boolean"
          },
          "confirmation": {
            "enum": [
              "callback",
              "polling",
              "both"
            ],
            "default": "callback"
          },
          "expected_latency_ms": {
            "type": "integer"
          },
          "timeout_ms": {
            "type": "integer",
            "minimum": 1,
            "maximum": 300000,
            "default": 30000
          },
          "pattern": {
            "type": "string",
            "enum": [
              "outbound_sync",
              "outbound_async",
              "outbound_notification",
              "inbound_request",
              "inbound_callback",
              "inbound_file",
              "outbound_file",
              "status_query"
            ],
            "description": "How the interaction happened (PSP-7.1); absent for internal work."
          }
        }
      },
      "CapabilityMatrix": {
        "type": "object",
        "properties": {
          "types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CapabilityRow"
            }
          }
        },
        "description": "Rules: amounts fit each currency's exponent; min_amount <= max_amount <= daily_limit <= monthly_limit; a type that needs a parent has one enabled (CAPTURE needs AUTH)."
      },
      "CodeEntry": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "A KulPay failure code; empty for approved or pending."
          },
          "family": {
            "enum": [
              "approved",
              "soft_decline",
              "hard_decline",
              "pending",
              "unknown",
              "error"
            ]
          },
          "description": {
            "type": "string"
          }
        }
      },
      "CodeMapping": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "enum": [
              "pending",
              "active",
              "superseded",
              "rejected"
            ]
          },
          "reason": {
            "type": "string"
          },
          "approval_id": {
            "type": "string"
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UnmappedCode": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string"
          },
          "raw_code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          },
          "first_seen": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}
