{
  "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": "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": "Incoming transfers",
      "description": "Transfers a bank or PSP certified for `account_funding.incoming` reports as received for a KulPay beneficiary (integration plan phase 5B): resolved privately, final only when the partner says so, and held in suspense until an operator resolves it when nobody matches."
    },
    {
      "name": "Payment acceptance",
      "description": "KulOffice phase 6: merchant applications (API keys belong to one), connector binding allowlists, the safe binding projection, and secure connector-configuration sessions through which a merchant hands its own provider credentials to the Gateway. Management operations live on the management surface; the hosted page and its JSON submission are public, the one-time session token being the capability."
    }
  ],
  "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. The `payment_id` stays bound to its amount, currency, method, payer and metadata for as long as the payment exists, beyond the 24-hour response replay: the same request returns the payment, another is `409 idempotency_key_reused`. `GET /payments?payment_id=` reads it back. The response carries the collection evidence (`mode`, `amount_minor`, `connector_binding_id`, `connector_ref`, and `settled_at` / `settled_amount_minor` once a live payment's binding confirmed it). 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`. The launch capability matrix (plan phase 10) holds the route first: one it does not enable is `422 route_not_enabled`, and anything off_net `422 movement_scope_disabled`, before any limit is reserved or provider called.",
        "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": "demoexample.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": "demoexample.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. Or the route is not in the launch matrix (route_not_enabled, movement_scope_disabled).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "default": {
                    "summary": "As documented",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "route_not_enabled": {
                    "summary": "Route not in the launch matrix",
                    "value": {
                      "type": "about:blank",
                      "title": "Route not enabled",
                      "status": 422,
                      "code": "route_not_enabled",
                      "detail": "the charge route settling/mpesa (live, acc_top) is not enabled in launch matrix 2026-10-12.1",
                      "rule": "launch_matrix",
                      "rejection_id": "3KfS0route0not0enabled00001"
                    }
                  }
                }
              }
            }
          },
          "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."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused before any provider call: the connector binding for this payment is restricted to other merchant applications (application_not_allowed), or the API key's application is disabled (application_inactive).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Application not allowed",
                  "status": 403,
                  "code": "application_not_allowed",
                  "detail": "Refused before any provider call: the connector binding for this payment is restricted to other merchant applications (application_not_allowed), or the API key's application is disabled (application_inactive).",
                  "error": {
                    "code": "application_not_allowed",
                    "message": "Refused before any provider call: the connector binding for this payment is restricted to other merchant applications (application_not_allowed), or the API key's application is disabled (application_inactive)."
                  },
                  "rejection_id": "3KUXS1aB2cD3eF4gH5iJ6kL7mN8",
                  "rule": "application"
                }
              }
            }
          }
        }
      }
    },
    "/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": "Sandbox only: an in-memory payout settled from fixtures; it calls no provider and moves no money. With a database it answers `404 not_found`; send payouts with `POST /v1/payouts`. In the sandbox the gateway returns the payout and posts a webhook when it 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"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/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"
          }
        },
        "description": "Sandbox only: reads a payout created by `POST /payouts`. With a database it answers `404 not_found`; read payouts with `GET /v1/payouts/{id}`."
      }
    },
    "/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"
                        }
                      }
                    },
                    "license": {
                      "type": "object",
                      "description": "The software licence (KULPAY_LICENSE_KEY) as of this request; the key itself is never returned. `enforced` is true when the gateway serves live payments, where a valid licence is required to start.",
                      "properties": {
                        "state": {
                          "type": "string",
                          "enum": ["valid", "expiring", "expired", "invalid", "missing"]
                        },
                        "enforced": {
                          "type": "boolean"
                        },
                        "product": {
                          "type": "string"
                        },
                        "id": {
                          "type": "string"
                        },
                        "issued_to": {
                          "type": "string"
                        },
                        "issuer": {
                          "type": "string"
                        },
                        "issued_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "days_left": {
                          "type": "integer"
                        },
                        "capabilities": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "error": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "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
                  },
                  "license": {
                    "state": "missing",
                    "enforced": false
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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",
                  "legacy_code": "gateway.statement_import_failed",
                  "error": {
                    "code": "gateway.statement_import_failed",
                    "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",
                  "legacy_code": "gateway.close_refused",
                  "error": {
                    "code": "gateway.close_refused",
                    "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",
                  "legacy_code": "gateway.resolve_failed",
                  "error": {
                    "code": "gateway.resolve_failed",
                    "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.",
                  "legacy_code": "gateway.not_found",
                  "error": {
                    "code": "gateway.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).",
                  "legacy_code": "replay_failed",
                  "error": {
                    "code": "replay_failed",
                    "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. Verification admits a callback; only a driver that authenticates its own callbacks (Stripe's signature, kulpay_balance's read-back) is applied as sent. M-Pesa and e-Mola callbacks are wake-ups: the payment moves only on the provider's answer to a status query (IG-82). 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.",
                  "legacy_code": "gateway.callback_rejected",
                  "error": {
                    "code": "gateway.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.",
                  "legacy_code": "gateway.not_found",
                  "error": {
                    "code": "gateway.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 (only the account's approved settlement destination, else 422 `payout_destination_not_approved`), `refund` refunds one of your own transactions (`original_transaction_id`). `cash_in` answers 422 `not_supported` until cash deposits are available. 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.*). `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`. 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"
                    }
                  }
                }
              },
              "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"
                      ]
                    },
                    "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": "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"
                      ]
                    },
                    "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": "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`, `payout_destination_not_approved` (a payout to anything but the approved settlement destination) or `not_refundable`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "default": {
                    "summary": "As documented",
                    "value": {
                      "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."
                      }
                    }
                  },
                  "payout_destination_not_approved": {
                    "summary": "Not the approved settlement destination",
                    "value": {
                      "type": "about:blank",
                      "title": "Payout destination not approved",
                      "status": 422,
                      "code": "payout_destination_not_approved",
                      "detail": "Payouts go only to the account's approved settlement destination (method and wallet or account); nothing was reserved or debited.",
                      "error": {
                        "code": "payout_destination_not_approved",
                        "message": "Payouts go only to the account's approved settlement destination (method and wallet or account); nothing was reserved or debited."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "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",
                  "legacy_code": "gateway.invalid_state",
                  "error": {
                    "code": "gateway.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`.",
        "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": {
      "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. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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"
                ],
                "expected_monthly_volume": 5000,
                "expected_monthly_value": {
                  "amount": "1000000",
                  "currency": "MZN"
                },
                "technical_contact": {
                  "name": "Ana",
                  "email": "opsexample.co.mz"
                },
                "compliance_contact": {
                  "name": "Rui",
                  "email": "opsexample.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"
                    ],
                    "expected_monthly_volume": 5000,
                    "expected_monthly_value": {
                      "amount": "1000000",
                      "currency": "MZN"
                    },
                    "technical_contact": {
                      "name": "Ana",
                      "email": "opsexample.co.mz"
                    },
                    "compliance_contact": {
                      "name": "Rui",
                      "email": "opsexample.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)."
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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": "opsexample.co.mz",
                      "phone": "+258841234567"
                    },
                    "compliance_contact": {
                      "name": "Electricidade de Mo\u00e7ambique",
                      "email": "opsexample.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"
                ],
                "expected_monthly_volume": 5000,
                "expected_monthly_value": {
                  "amount": "1000000",
                  "currency": "MZN"
                },
                "technical_contact": {
                  "name": "Ana",
                  "email": "opsexample.co.mz"
                },
                "compliance_contact": {
                  "name": "Rui",
                  "email": "opsexample.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"
                    ],
                    "expected_monthly_volume": 5000,
                    "expected_monthly_value": {
                      "amount": "1000000",
                      "currency": "MZN"
                    },
                    "technical_contact": {
                      "name": "Ana",
                      "email": "opsexample.co.mz"
                    },
                    "compliance_contact": {
                      "name": "Rui",
                      "email": "opsexample.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, routing, retry_policy 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`.",
        "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`).",
            "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"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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}/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 and void flows, then idempotency replay, webhook delivery, redelivery and a badly signed webhook to the PSP's endpoint, and the sample statement (CSV) matched against the run's payments. With `rerun_of`, only that run's failed cases run again; the passed ones are carried over; with `only_case` as well, only that one case. When every mandatory case passes, a report is signed (audit signing key). With no case failed the PSP is certified and, from technical_setup, moves to certification. With optional cases failed the run is passed but does not certify: it records psp.certification.exceptions, and the PSP is certified once an operator prepares the acceptance of those cases (approval psp.certification.accept) and a second one approves it, or after a run where they pass. Records psp.certification.run_started, case_failed, passed or failed. Requires `accounts:write`.",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "only_case": {
                    "type": "string",
                    "description": "With rerun_of: repeat only this failed case (its key); every other case is carried over as it was."
                  },
                  "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": {
                  "only_case": {
                    "type": "string",
                    "description": "With rerun_of: repeat only this failed case (its key); every other case is carried over as it was."
                  },
                  "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, and for a PSP certification passed), live gates (7 days in pilot since entry or the last critical incident resolved, no open critical incident, and for a PSP live statements since pilot all matched, for an LSP a live remittance paid to its biller), each with `detail` and `detail_pt` (Portuguese). Once the live gates are met the gateway prepares the go-live request by itself; a second person approves it, 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.",
        "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
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/psps/{id}/requirements": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getPartnerRequirements",
        "summary": "Your requirements",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pt",
                "en"
              ]
            },
            "description": "The language of the names and help (pt by default)."
          }
        ],
        "description": "For the partner the key belongs to (scope `psp:configure`): what is past due, due now, due later, waiting for someone, refused, done and optional, each with its stage, who does it, its deadline and the capability it protects; and the capabilities closed by a past-due requirement set to block. The stages advance by themselves once a stage's required requirements are met. Also in the portal (Requisitos).",
        "responses": {
          "200": {
            "description": "The requirements.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerRequirements"
                },
                "example": {
                  "psp_id": "3KWPlauKgv7rrVi1Wsue46ebEDF",
                  "stage": "technical",
                  "past_due": [],
                  "currently_due": [
                    {
                      "key": "webhooks",
                      "name": "Webhooks validated",
                      "type": "configuration",
                      "required": true,
                      "stage": "technical",
                      "who": "partner",
                      "blocks": "pilot",
                      "on_past_due": "warn",
                      "status": "currently_due",
                      "due_by": "2026-11-04T09:00:00Z"
                    }
                  ],
                  "eventually_due": [],
                  "pending_verification": [],
                  "errors": [],
                  "done": [],
                  "optional": [],
                  "blocked": []
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/psps/{id}/requirements": {
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getAdminRequirements",
        "summary": "A partner's requirements",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pt",
                "en"
              ]
            },
            "description": "The language of the names and help (pt by default)."
          }
        ],
        "description": "Any partner's requirements, as GET /v1/psps/{id}/requirements gives its own. Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The requirements.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerRequirements"
                },
                "example": {
                  "psp_id": "3KWPlauKgv7rrVi1Wsue46ebEDF",
                  "stage": "technical",
                  "past_due": [],
                  "currently_due": [
                    {
                      "key": "webhooks",
                      "name": "Webhooks validated",
                      "type": "configuration",
                      "required": true,
                      "stage": "technical",
                      "who": "partner",
                      "blocks": "pilot",
                      "on_past_due": "warn",
                      "status": "currently_due",
                      "due_by": "2026-11-04T09:00:00Z"
                    }
                  ],
                  "eventually_due": [],
                  "pending_verification": [],
                  "errors": [],
                  "done": [],
                  "optional": [],
                  "blocked": []
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/catalogs": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "listCatalogs",
        "summary": "The catalogues in force",
        "security": [],
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "psp",
                "lsp"
              ]
            },
            "description": "Only the items offered to this kind of partner."
          }
        ],
        "description": "Every back-office catalogue in force, with its items: the codes the applications and the APIs accept (partner_types, psp_roles, transaction_types, countries, currencies, biller_sectors, kyb_documents, webhook_events, statement_formats, statement_delivery, statement_frequencies, orientation_patterns, the reasons catalogues, requirements, screening_lists). Public; cached for 60 seconds.",
        "responses": {
          "200": {
            "description": "The catalogues.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Catalog"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "code": "partner_types",
                      "name": "Tipos de parceiro",
                      "regulatory": false,
                      "bound": false,
                      "version": 0,
                      "items": [
                        {
                          "code": "bank",
                          "name": {
                            "pt": "Banco",
                            "en": "Bank"
                          },
                          "order": 1,
                          "applies_to": [
                            "psp"
                          ]
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/catalogs/{code}": {
      "get": {
        "tags": [
          "Reference"
        ],
        "operationId": "getCatalog",
        "summary": "One catalogue in force",
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "psp",
                "lsp"
              ]
            },
            "description": "Only the items offered to this kind of partner."
          }
        ],
        "description": "One catalogue and its items, e.g. GET /v1/catalogs/partner_types?kind=psp for the partner types a PSP chooses from at application.",
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                },
                "example": {
                  "code": "partner_types",
                  "name": "Tipos de parceiro",
                  "regulatory": false,
                  "bound": false,
                  "version": 0,
                  "items": [
                    {
                      "code": "bank",
                      "name": {
                        "pt": "Banco",
                        "en": "Bank"
                      },
                      "order": 1,
                      "applies_to": [
                        "psp"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/catalogs/{code}/versions": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "listCatalogVersions",
        "summary": "A catalogue's versions",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "Every version of a catalogue, newest first: pending, active, superseded or rejected, with who proposed it and why. Requires `accounts:read`.",
        "responses": {
          "200": {
            "description": "The versions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CatalogVersion"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KWPpkrJxAYv4GusdsaY9P0DQj5",
                      "catalog": "partner_types",
                      "version": 2,
                      "status": "pending",
                      "reason": "microfinance partners",
                      "approval_id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                      "created_by": "operator:ana",
                      "created_at": "2026-10-21T09:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "proposeCatalog",
        "summary": "Propose a catalogue version",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "description": "The whole list of items, in order, with a reason. It becomes active once a second person approves it (approval catalog.activate, or catalog.regulatory.activate with `compliance:approve` for a regulatory catalogue). Codes are stable: an item is retired, never deleted. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items",
                  "reason"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/CatalogItem"
                    }
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "items": [
                  {
                    "code": "bank",
                    "name": {
                      "pt": "Banco",
                      "en": "Bank"
                    },
                    "order": 1,
                    "applies_to": [
                      "psp"
                    ]
                  },
                  {
                    "code": "microfinance",
                    "name": {
                      "pt": "Microfinan\u00e7as",
                      "en": "Microfinance"
                    },
                    "order": 2,
                    "applies_to": [
                      "psp"
                    ]
                  }
                ],
                "reason": "microfinance partners"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Proposed; waits for a second person.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "$ref": "#/components/schemas/CatalogVersion"
                    },
                    "approval": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "version": {
                    "id": "3KWPpkrJxAYv4GusdsaY9P0DQj5",
                    "catalog": "partner_types",
                    "version": 2,
                    "status": "pending",
                    "reason": "microfinance partners",
                    "approval_id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                    "created_by": "operator:ana",
                    "created_at": "2026-10-21T09:00:00Z"
                  },
                  "approval": {
                    "id": "3KWPphBZ9VTn7DG5zPpow7M3U5Q",
                    "kind": "catalog.activate",
                    "subject_id": "partner_types",
                    "reason": "microfinance partners",
                    "prepared_by": "operator:ana",
                    "status": "pending",
                    "created_at": "2026-10-21T09:00:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/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). 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. The launch capability matrix (plan phase 10) holds the route first: one it does not enable is `422 route_not_enabled`, and anything off_net `422 movement_scope_disabled`, before any limit is reserved or provider called. Payouts go only to the account's approved settlement destination (the settlement method and wallet or account, set by a settlement change a second person approved; IG-26a, owner's decision of 2026-10-12): the method, the beneficiary's type (an account for bank, an MSISDN for a wallet) and its number, normalized, must match. Anything else, or an account without an approved destination, is `422 payout_destination_not_approved`, before any limit is reserved, any balance debited or any journal entry written. The rule applies at acceptance: a replay of an accepted payout returns it, and one already accepted (e.g. waiting for approval) is sent.",
        "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": {
                    "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)."
                      }
                    }
                  }
                }
              },
              "example": {
                "amount": "300",
                "currency": "MZN",
                "method": "mpesa",
                "beneficiary": {
                  "msisdn": "258841234567",
                  "name": "Joao"
                },
                "description": "invoice 9"
              }
            }
          }
        },
        "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"
                      ]
                    }
                  }
                },
                "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, or no Idempotency-Key.",
            "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`. Or the route is not in the launch matrix (route_not_enabled, movement_scope_disabled). Or the destination is not the account's approved settlement destination (payout_destination_not_approved).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "default": {
                    "summary": "As documented",
                    "value": {
                      "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."
                      }
                    }
                  },
                  "route_not_enabled": {
                    "summary": "Route not in the launch matrix",
                    "value": {
                      "type": "about:blank",
                      "title": "Route not enabled",
                      "status": 422,
                      "code": "route_not_enabled",
                      "detail": "the charge route settling/mpesa (live, acc_top) is not enabled in launch matrix 2026-10-12.1",
                      "rule": "launch_matrix",
                      "rejection_id": "3KfS0route0not0enabled00001"
                    }
                  },
                  "payout_destination_not_approved": {
                    "summary": "Not the approved settlement destination",
                    "value": {
                      "type": "about:blank",
                      "title": "Payout destination not approved",
                      "status": 422,
                      "code": "payout_destination_not_approved",
                      "detail": "Payouts go only to the account's approved settlement destination (method and wallet or account); nothing was reserved or debited.",
                      "error": {
                        "code": "payout_destination_not_approved",
                        "message": "Payouts go only to the account's approved settlement destination (method and wallet or account); nothing was reserved or debited."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "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`.",
        "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"
                    }
                  }
                },
                "example": {
                  "available": "1020",
                  "currency": "MZN",
                  "mode": "test"
                }
              }
            }
          }
        }
      }
    },
    "/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:checkerexample.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:checkerexample.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:checkerexample.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: with the deployment's collecting auto-payout off (the default, `settlement.collecting_auto_payout`; G14) a batch with a net to pay stops at `ready` and becomes `settled` once an operator's record of its external settlement is approved; with it on, the net is paid to the settlement destination (`paid`). Either way it is carried into the next batch when it cannot be paid (`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`.",
        "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"
                          }
                        }
                      }
                    },
                    "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, reserves), the transaction and the amount. A batch never changes once built. Requires `settlements:read`. A managed pass-through batch (`funds_flow` managed, plan phase 8) is built from the merchant's settlement arrangement: its lines are each payable's gross, platform fee, provider cost and reserve, its adjustments, reserves released and receivables offset; it stops at `ready` (no payout) and becomes `settled` when an external settlement is recorded.",
        "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"
                        },
                        "arrangement_id": {
                          "type": "string"
                        },
                        "arrangement_version": {
                          "type": "integer"
                        },
                        "reserves": {
                          "type": "string"
                        },
                        "external_reference": {
                          "type": "string"
                        },
                        "settled_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "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"
                          },
                          "payable_id": {
                            "type": "string"
                          },
                          "receivable_id": {
                            "type": "string"
                          },
                          "amount_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "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. Collecting batches are paid out only when `settlement.collecting_auto_payout` is on (then ready ones left from while it was off are paid too); off (the default), they stop at `ready`. 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, 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"
                        }
                      }
                    }
                  }
                },
                "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` (the account number or IBAN). 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"
                  }
                }
              },
              "example": {
                "cycle_days": 0,
                "method": "string",
                "destination": "string",
                "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": "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",
                            "h2h"
                          ]
                        },
                        "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",
                          "h2h"
                        ]
                      },
                      "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}"
                }
              }
            }
          }
        }
      }
    },
    "/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`. Cash deposits are an operation of the launch capability matrix (`cash_deposit`, IG-52): the default matrix (`2026-10-12.3`) enables them in test mode only, and a mode it does not enable is refused with 422 `route_not_enabled` before anything is stored.",
        "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"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`route_not_enabled`: the launch capability matrix does not enable cash deposits in this mode (the default matrix disables live mode; IG-52). Nothing was stored.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Route not enabled",
                  "status": 422,
                  "code": "route_not_enabled",
                  "rule": "launch_matrix",
                  "detail": "the cash_deposit route partner_api/cash (live, acc_top) is not enabled in launch matrix 2026-10-12.3"
                }
              }
            }
          }
        }
      },
      "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`. Assigning (which credits the account) is refused with 422 `route_not_enabled`, at the request and at the approval, while the launch matrix does not enable cash deposits in the deposit's mode (IG-52); returning is not.",
        "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"
                  }
                }
              },
              "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"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`route_not_enabled`: the launch capability matrix does not enable cash deposits in this mode (the default matrix disables live mode; IG-52). Nothing was stored.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Route not enabled",
                  "status": 422,
                  "code": "route_not_enabled",
                  "rule": "launch_matrix",
                  "detail": "the cash_deposit route partner_api/cash (live, acc_top) is not enabled in launch matrix 2026-10-12.3"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/cash-in-incoming": {
      "put": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "putCashInIncoming",
        "summary": "Record an account's cash deposits as incoming transfers",
        "description": "On, each cash deposit credited to the account and initiated against a customer's reference (not the account's own ID or tag) is also recorded as an incoming transfer: from the partner that took the cash, matched to the account, confirmed final, its destination the reference looked up. For an account collecting for its own customers (KulOffice). Off by default. Records `account.cash_in_incoming.set`. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "enabled (true or false) is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "enabled (true or false) is required"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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",
                  "legacy_code": "fetch_failed",
                  "error": {
                    "code": "fetch_failed",
                    "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.macuacuaexample.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.macuacuaexample.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.macuacuaexample.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.macuacuaexample.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": "Mark a report submitted",
        "description": "Only a reconciled report (`regulatory_report.submitted`). Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "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"
                    },
                    "submitted_by": {
                      "type": "string"
                    },
                    "submitted_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "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": "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"
                }
              }
            }
          },
          "409": {
            "description": "Blocked or already submitted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_submittable",
                  "detail": "blocked: totals do not reconcile to the ledger: MZN: report 98750000, ledger 98625000",
                  "error": {
                    "code": "not_submittable",
                    "message": "blocked: totals do not reconcile to the ledger: MZN: report 98750000, ledger 98625000"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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 with the same amount returns it; another amount is 409 `lookup_used`); 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. For a bill (BIL-5.11) the lookup must be `found` (choose an ambiguous one's option first), the amount includes the payer's fee, and the bill is checked to still take it before you take the money; the intent carries `destination_kind`. Records `cash_in.initiated`. Requires `transactions:write` and an `Idempotency-Key`. Cash deposits are an operation of the launch capability matrix (`cash_deposit`, IG-52): the default matrix (`2026-10-12.3`) enables them in test mode only, and a mode it does not enable is refused with 422 `route_not_enabled` before anything is stored.",
        "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",
                    "description": "Free-form facts about the depositor. For a bill, phone and email are where KulPay sends the token or PIN and the receipt link once it is paid (BIL-5.11).",
                    "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"
                        }
                      }
                    }
                  }
                },
                "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.macuacuaexample.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. Also `route_not_enabled`: the launch matrix does not enable cash deposits in this mode (IG-52).",
            "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": {}
                          }
                        }
                      }
                    },
                    "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"
                        }
                      }
                    }
                  }
                },
                "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. For a bill (BIL-5.11) your confirmation is trusted: the money is collected and the bill is paid at once, with you as its channel (`funding: deposit`); the answer adds `bill` (its status, amounts, `receipt_url` and any token or PIN to give the payer), the intent becomes `completed` (or `processing` while the supplier answers, then `cash_in.completed`), and the deposit is `confirmed` with `recon_status` `pending` until the statement line reconciles it; a difference is an exception for treasury and the payment stands. A bill that cannot be paid makes the intent `failed` and puts the money in suspense for treasury to return (`cash_in.completed`, `cash_in.suspended`). Records `cash_in.initiation_confirmed` and `cash_in.reported`. Requires `transactions:write` and an `Idempotency-Key`. Cash deposits are an operation of the launch capability matrix (`cash_deposit`, IG-52): the default matrix (`2026-10-12.3`) enables them in test mode only, and a mode it does not enable is refused with 422 `route_not_enabled` before anything is stored.",
        "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"
                            }
                          }
                        }
                      }
                    },
                    "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. Also `route_not_enabled`: the launch matrix does not enable cash deposits in this mode (IG-52).",
            "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"
                                      },
                                      "price_minor": {
                                        "type": "integer",
                                        "format": "int64",
                                        "example": 150000,
                                        "description": "The price in exact minor units of `currency` (IG-35)."
                                      },
                                      "currency": {
                                        "type": "string",
                                        "example": "MZN",
                                        "description": "ISO 4217; the product's currency."
                                      },
                                      "validity": {
                                        "type": "string",
                                        "example": "30d"
                                      }
                                    }
                                  },
                                  "description": "The product's active offers, cheapest first; inactive offers are not listed."
                                },
                                "purchase_modes": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "enum": [
                                      "fixed_catalog",
                                      "variable_amount"
                                    ]
                                  },
                                  "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "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",
                              "price_minor": 75000,
                              "currency": "MZN",
                              "validity": "30d"
                            },
                            {
                              "code": "TVPREMIUM",
                              "name": "Pacote Premium",
                              "price": "2500.00",
                              "price_minor": 250000,
                              "currency": "MZN",
                              "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"
                                },
                                "price_minor": {
                                  "type": "integer",
                                  "format": "int64",
                                  "example": 150000,
                                  "description": "The price in exact minor units of `currency` (IG-35)."
                                },
                                "currency": {
                                  "type": "string",
                                  "example": "MZN",
                                  "description": "ISO 4217; the product's currency."
                                },
                                "validity": {
                                  "type": "string",
                                  "example": "30d"
                                }
                              }
                            },
                            "description": "The product's active offers, cheapest first; inactive offers are not listed."
                          },
                          "purchase_modes": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "fixed_catalog",
                                "variable_amount"
                              ]
                            },
                            "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                          }
                        }
                      }
                    }
                  }
                },
                "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",
                          "price_minor": 75000,
                          "currency": "MZN",
                          "validity": "30d"
                        },
                        {
                          "code": "TVPREMIUM",
                          "name": "Pacote Premium",
                          "price": "2500.00",
                          "price_minor": 250000,
                          "currency": "MZN",
                          "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/billers/{code}/products/{product}/offers": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "listProductOffers",
        "summary": "A product's offers",
        "description": "The active offers of one product you may sell (IG-35), cheapest first, each with its decimal `price`, the exact `price_minor` and the product's `currency`, and the product's `purchase_modes`. Inactive offers are not listed. Responses carry an `ETag` and `Cache-Control: max-age=300`; send `If-None-Match` to revalidate. Requires `bills:read`.",
        "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": "`{data, purchase_modes}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "code": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "price": {
                            "type": "string",
                            "example": "1500.00"
                          },
                          "price_minor": {
                            "type": "integer",
                            "format": "int64",
                            "example": 150000,
                            "description": "The price in exact minor units of `currency` (IG-35)."
                          },
                          "currency": {
                            "type": "string",
                            "example": "MZN",
                            "description": "ISO 4217; the product's currency."
                          },
                          "validity": {
                            "type": "string",
                            "example": "30d"
                          }
                        }
                      }
                    },
                    "purchase_modes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "fixed_catalog",
                          "variable_amount"
                        ]
                      },
                      "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "code": "TVBASIC",
                      "name": "Pacote B\u00e1sico",
                      "price": "750.00",
                      "price_minor": 75000,
                      "currency": "MZN",
                      "validity": "30d"
                    },
                    {
                      "code": "TVPREMIUM",
                      "name": "Pacote Premium",
                      "price": "2500.00",
                      "price_minor": 250000,
                      "currency": "MZN",
                      "validity": "30d"
                    }
                  ],
                  "purchase_modes": [
                    "fixed_catalog",
                    "variable_amount"
                  ]
                }
              }
            }
          },
          "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": "apoioexample.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": "clientesexample.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."
                  }
                }
              },
              "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/reference-routes": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminReferenceRoutes",
        "summary": "Where a reference would be routed (operators)",
        "description": "Which billers a reference lookup that names no biller would ask (BIL-5.14): the active billers whose routable products' reference_pattern matches, for a channel when given. `too_many` when more than 5 match (the lookup answers biller_required). Requires `connectors:read`.",
        "parameters": [
          {
            "name": "reference",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A channel account: only products open to it."
          }
        ],
        "responses": {
          "200": {
            "description": "The route.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reference": {
                      "type": "string"
                    },
                    "rf": {
                      "type": "boolean"
                    },
                    "too_many": {
                      "type": "boolean"
                    },
                    "then": {
                      "type": "string"
                    },
                    "billers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "products": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "reference": "01123456782",
                  "rf": false,
                  "too_many": false,
                  "then": "these billers, in parallel; the deposit resolver if none knows it",
                  "billers": [
                    {
                      "biller": "EDM",
                      "biller_name": "Electricidade de Mo\u00e7ambique",
                      "products": [
                        "CREDELEC"
                      ]
                    },
                    {
                      "biller": "LUZ",
                      "biller_name": "Luz do Norte",
                      "products": [
                        "CONTA"
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A reference of 3 to 128 characters is required.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a reference is 3 to 128 characters"
                }
              }
            }
          }
        }
      }
    },
    "/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"
                          },
                          "routable": {
                            "type": "boolean",
                            "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                          },
                          "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"
                          },
                          "purchase_modes": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "fixed_catalog",
                                "variable_amount"
                              ]
                            },
                            "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                          }
                        }
                      }
                    }
                  }
                },
                "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; 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."
                  }
                }
              },
              "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}/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"
                  },
                  "routable": {
                    "type": "boolean",
                    "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                  },
                  "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"
                  },
                  "purchase_modes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ]
                    },
                    "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                  }
                }
              },
              "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"
                    },
                    "routable": {
                      "type": "boolean",
                      "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                    },
                    "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"
                    },
                    "purchase_modes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "fixed_catalog",
                          "variable_amount"
                        ]
                      },
                      "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                    }
                  }
                },
                "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"
                  },
                  "routable": {
                    "type": "boolean",
                    "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                  },
                  "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"
                  },
                  "purchase_modes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ]
                    },
                    "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                  }
                }
              },
              "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"
                    },
                    "routable": {
                      "type": "boolean",
                      "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                    },
                    "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"
                    },
                    "purchase_modes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "fixed_catalog",
                          "variable_amount"
                        ]
                      },
                      "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                    }
                  }
                },
                "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"
                    },
                    "routable": {
                      "type": "boolean",
                      "description": "A lookup that names no biller is sent to this biller when the reference matches reference_pattern (BIL-5.14); needs reference_pattern."
                    },
                    "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"
                    },
                    "purchase_modes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "fixed_catalog",
                          "variable_amount"
                        ]
                      },
                      "description": "How the product may be bought: `fixed_catalog` (an offer from its catalogue) and/or `variable_amount` (an amount under `amount_rule`). Both by default; a catalogue-only product needs no `amount_rule`."
                    }
                  }
                },
                "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`.",
        "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"
                    },
                    "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 supply connector (`supplier_connector`), the product version and the product's code at the supplier that produced it, with a digest of its terms: it is paid as issued or not at all. `purchase_mode` says whether it was priced from the catalogue (`offer`) or as an amount; a product may sell either or both (`purchase_modes` on the listing). To price it again, refresh it (`POST /v1/bill-payments/quotes/{id}/refresh`): a new quote from the same connector, the old one unchanged. `422 amount_out_of_range` gives the allowed range. Requires `bills:write`. When the provider states them, the quote also carries the bill's open amount (`outstanding_minor`) and whether it may be paid in part (`allow_partial`), as stated at quote time. When the provider describes the bill, the quote carries `bill_details` (holder, service address, billing period, issue and due dates) for display; they are not terms.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "biller",
                  "product"
                ],
                "properties": {
                  "biller": {
                    "type": "string"
                  },
                  "product": {
                    "type": "string"
                  },
                  "customer_reference": {
                    "type": "string"
                  },
                  "lookup_id": {
                    "type": "string"
                  },
                  "amount": {
                    "type": "string",
                    "example": "250.00"
                  },
                  "offer": {
                    "type": "string",
                    "description": "An offer code from the product's catalogue (`fixed_catalog`); leave out for an amount (`variable_amount`)."
                  }
                }
              },
              "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"
                      ]
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    },
                    "outstanding_minor": {
                      "type": "integer",
                      "format": "int64",
                      "example": 150000,
                      "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                    },
                    "allow_partial": {
                      "type": "boolean",
                      "example": false,
                      "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                    },
                    "bill_details": {
                      "type": "object",
                      "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                      "properties": {
                        "holder_name": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Ana Nhantumbo",
                          "description": "The bill's holder as the provider names them."
                        },
                        "service_address": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Av. 24 de Julho 1020, Maputo",
                          "description": "Where the service is supplied."
                        },
                        "period_start": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-01",
                          "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-30",
                          "description": "Last day of the billing period (YYYY-MM-DD)."
                        },
                        "issued_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-01",
                          "description": "When the bill was issued (YYYY-MM-DD)."
                        },
                        "due_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-15",
                          "description": "When the bill is due (YYYY-MM-DD)."
                        }
                      }
                    }
                  }
                },
                "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"
                    }
                  ],
                  "amount_minor": 25000,
                  "payer_fee_minor": 500,
                  "total_minor": 25500,
                  "purchase_mode": "variable_amount",
                  "supplier_connector": "edm-live",
                  "payment_status": "none",
                  "fulfilment": {
                    "status": "not_started",
                    "attempts": 0
                  }
                }
              }
            }
          },
          "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/quotes/{id}/refresh": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "refreshBillQuote",
        "summary": "Refresh a bill quote",
        "description": "Prices a quote again at the supply connector that issued it (design: a refresh creates a new quotation) and returns a new quote, with `refreshed_from` naming the old one. The old quote is never changed, whatever its state (quoted, expired, paid): what a payer accepted stays as accepted. The reference is looked up again at the same connector when the product requires it; the offer or amount is the old quote's. If that connector is disabled or no longer sells the product, the refresh is refused (`409 quote_invalidated` or `503 biller_unavailable`) rather than priced elsewhere: request a new quote instead. An invoice is quoted again with `invoice_id`. Requires `bills:write`.",
        "responses": {
          "201": {
            "description": "The new 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"
                      ]
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    },
                    "outstanding_minor": {
                      "type": "integer",
                      "format": "int64",
                      "example": 150000,
                      "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                    },
                    "allow_partial": {
                      "type": "boolean",
                      "example": false,
                      "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                    },
                    "bill_details": {
                      "type": "object",
                      "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                      "properties": {
                        "holder_name": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Ana Nhantumbo",
                          "description": "The bill's holder as the provider names them."
                        },
                        "service_address": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Av. 24 de Julho 1020, Maputo",
                          "description": "Where the service is supplied."
                        },
                        "period_start": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-01",
                          "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-30",
                          "description": "Last day of the billing period (YYYY-MM-DD)."
                        },
                        "issued_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-01",
                          "description": "When the bill was issued (YYYY-MM-DD)."
                        },
                        "due_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-15",
                          "description": "When the bill is due (YYYY-MM-DD)."
                        }
                      }
                    }
                  }
                },
                "example": {
                  "id": "3KWPl9Qy0bWm2ZkT4dNfE1sH7aP",
                  "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": "24.00",
                    "unit_type": "kWh",
                    "lines": [
                      {
                        "concept": "Unit charge per kWh",
                        "units": "24.00",
                        "price": "10.42",
                        "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"
                    }
                  ],
                  "amount_minor": 25000,
                  "payer_fee_minor": 500,
                  "total_minor": 25500,
                  "purchase_mode": "variable_amount",
                  "supplier_connector": "edm-live",
                  "payment_status": "none",
                  "fulfilment": {
                    "status": "not_started",
                    "attempts": 0
                  },
                  "refreshed_from": "3KWPl4pk0XxRI4TEVI6avRgrj1y"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: an invoice quote (quote the invoice again with `invoice_id`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "an invoice is quoted again with invoice_id",
                  "error": {
                    "code": "invalid_request",
                    "message": "an invoice is quoted again with invoice_id"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such quote of 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 bill payment or quote",
                  "error": {
                    "code": "not_found",
                    "message": "no such bill payment or quote"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`quote_invalidated`: the connector that issued the quote no longer sells the product. Request a new quote.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "quote_invalidated",
                  "detail": "the supplier that issued this quote no longer sells Credelec; request a new quote",
                  "error": {
                    "code": "quote_invalidated",
                    "message": "the supplier that issued this quote no longer sells Credelec; request a new quote"
                  }
                }
              }
            }
          },
          "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
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The quote (bill payment) to refresh.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/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 as issued, through the supply connector that issued it, or not at all: a quote whose connector was disabled or withdrawn from the product, or whose terms no longer match, is refused before anything is reserved (`409 quote_invalidated`), and is never sent through another supplier; should that happen after acceptance but before sending, the payment fails with `quote_invalidated` and the reserve is released. `payment_status` (the money) and `fulfilment` (the supplier leg, with the evidence that settled it) are reported separately. 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`. The launch capability matrix (plan phase 10) holds the route first: one it does not enable is `422 route_not_enabled`, and anything off_net `422 movement_scope_disabled`, before any limit is reserved or provider called.",
        "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"
                      }
                    }
                  }
                }
              },
              "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"
                      ]
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    },
                    "outstanding_minor": {
                      "type": "integer",
                      "format": "int64",
                      "example": 150000,
                      "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                    },
                    "allow_partial": {
                      "type": "boolean",
                      "example": false,
                      "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                    },
                    "bill_details": {
                      "type": "object",
                      "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                      "properties": {
                        "holder_name": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Ana Nhantumbo",
                          "description": "The bill's holder as the provider names them."
                        },
                        "service_address": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Av. 24 de Julho 1020, Maputo",
                          "description": "Where the service is supplied."
                        },
                        "period_start": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-01",
                          "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-30",
                          "description": "Last day of the billing period (YYYY-MM-DD)."
                        },
                        "issued_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-01",
                          "description": "When the bill was issued (YYYY-MM-DD)."
                        },
                        "due_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-15",
                          "description": "When the bill is due (YYYY-MM-DD)."
                        }
                      }
                    }
                  }
                },
                "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"
                    }
                  ],
                  "amount_minor": 25000,
                  "payer_fee_minor": 500,
                  "total_minor": 25500,
                  "purchase_mode": "variable_amount",
                  "supplier_connector": "edm-live",
                  "accepted_at": "2026-10-10T22:14:58.70112Z",
                  "payment_status": "captured",
                  "fulfilment": {
                    "status": "succeeded",
                    "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                    "attempts": 1,
                    "supplier_reference": "FK2026101045631213",
                    "evidence": {
                      "source": "fulfil_response",
                      "authenticated_by": "connector_session",
                      "outcome": "succeeded",
                      "connector_id": "3KWPl3x9eQf0yX1oVJm2aQ8bLtR",
                      "driver": "edm",
                      "request_id": "kp-3KWPl4pk0XxRI4TEVI6avRgrj1y-1",
                      "supplier_reference": "FK2026101045631213",
                      "observed_at": "2026-10-10T22:14:58.744497415Z",
                      "result_sha256": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                    }
                  }
                }
              }
            }
          },
          "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"
                      ]
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    },
                    "outstanding_minor": {
                      "type": "integer",
                      "format": "int64",
                      "example": 150000,
                      "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                    },
                    "allow_partial": {
                      "type": "boolean",
                      "example": false,
                      "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                    },
                    "bill_details": {
                      "type": "object",
                      "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                      "properties": {
                        "holder_name": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Ana Nhantumbo",
                          "description": "The bill's holder as the provider names them."
                        },
                        "service_address": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Av. 24 de Julho 1020, Maputo",
                          "description": "Where the service is supplied."
                        },
                        "period_start": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-01",
                          "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-30",
                          "description": "Last day of the billing period (YYYY-MM-DD)."
                        },
                        "issued_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-01",
                          "description": "When the bill was issued (YYYY-MM-DD)."
                        },
                        "due_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-15",
                          "description": "When the bill is due (YYYY-MM-DD)."
                        }
                      }
                    }
                  }
                },
                "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`; or `quote_invalidated`: the quote no longer holds as issued (its supplier was disabled or withdrawn, or its terms changed). Request a new quote.",
            "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. Or the route is not in the launch matrix (route_not_enabled, movement_scope_disabled).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "default": {
                    "summary": "As documented",
                    "value": {
                      "type": "about:blank",
                      "title": "Unprocessable Content",
                      "status": 422,
                      "code": "insufficient_balance",
                      "detail": "the balance (10.00 MZN) does not cover 255.00 MZN",
                      "legacy_code": "insufficient_funds",
                      "error": {
                        "code": "insufficient_funds",
                        "message": "the balance (10.00 MZN) does not cover 255.00 MZN"
                      }
                    }
                  },
                  "route_not_enabled": {
                    "summary": "Route not in the launch matrix",
                    "value": {
                      "type": "about:blank",
                      "title": "Route not enabled",
                      "status": 422,
                      "code": "route_not_enabled",
                      "detail": "the charge route settling/mpesa (live, acc_top) is not enabled in launch matrix 2026-10-12.1",
                      "rule": "launch_matrix",
                      "rejection_id": "3KfS0route0not0enabled00001"
                    }
                  }
                }
              }
            }
          },
          "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"
                            ]
                          },
                          "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"
                          },
                          "amount_minor": {
                            "type": "integer",
                            "description": "`amount` in minor units.",
                            "example": 25000
                          },
                          "payer_fee_minor": {
                            "type": "integer",
                            "description": "`payer_fee` in minor units.",
                            "example": 500
                          },
                          "total_minor": {
                            "type": "integer",
                            "description": "`total` in minor units.",
                            "example": 25500
                          },
                          "purchase_mode": {
                            "type": "string",
                            "enum": [
                              "fixed_catalog",
                              "variable_amount"
                            ],
                            "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                          },
                          "supplier_connector": {
                            "type": "string",
                            "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                          },
                          "refreshed_from": {
                            "type": "string",
                            "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                          },
                          "accepted_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "When the quote was accepted (paid or collected)."
                          },
                          "payment_status": {
                            "type": "string",
                            "enum": [
                              "none",
                              "collecting",
                              "reserved",
                              "captured",
                              "released",
                              "refunded",
                              "refund_pending",
                              "not_collected"
                            ],
                            "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                          },
                          "fulfilment": {
                            "type": "object",
                            "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                            "properties": {
                              "status": {
                                "type": "string",
                                "enum": [
                                  "not_started",
                                  "submitted",
                                  "unknown",
                                  "succeeded",
                                  "failed"
                                ]
                              },
                              "request_id": {
                                "type": "string"
                              },
                              "attempts": {
                                "type": "integer"
                              },
                              "supplier_reference": {
                                "type": "string"
                              },
                              "evidence": {
                                "type": "object",
                                "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                                "properties": {
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "fulfil_response",
                                      "status_query",
                                      "operator_resolution"
                                    ]
                                  },
                                  "authenticated_by": {
                                    "type": "string",
                                    "enum": [
                                      "connector_session",
                                      "dual_approval"
                                    ]
                                  },
                                  "outcome": {
                                    "type": "string",
                                    "enum": [
                                      "succeeded",
                                      "failed"
                                    ]
                                  },
                                  "connector_id": {
                                    "type": "string"
                                  },
                                  "driver": {
                                    "type": "string"
                                  },
                                  "request_id": {
                                    "type": "string"
                                  },
                                  "supplier_reference": {
                                    "type": "string"
                                  },
                                  "supplier_code": {
                                    "type": "string",
                                    "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                                  },
                                  "approval_id": {
                                    "type": "string"
                                  },
                                  "observed_at": {
                                    "type": "string",
                                    "format": "date-time"
                                  },
                                  "result_sha256": {
                                    "type": "string",
                                    "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                                  }
                                }
                              }
                            }
                          },
                          "outstanding_minor": {
                            "type": "integer",
                            "format": "int64",
                            "example": 150000,
                            "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                          },
                          "allow_partial": {
                            "type": "boolean",
                            "example": false,
                            "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                          },
                          "bill_details": {
                            "type": "object",
                            "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                            "properties": {
                              "holder_name": {
                                "type": "string",
                                "maxLength": 200,
                                "example": "Ana Nhantumbo",
                                "description": "The bill's holder as the provider names them."
                              },
                              "service_address": {
                                "type": "string",
                                "maxLength": 200,
                                "example": "Av. 24 de Julho 1020, Maputo",
                                "description": "Where the service is supplied."
                              },
                              "period_start": {
                                "type": "string",
                                "format": "date",
                                "example": "2026-09-01",
                                "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                              },
                              "period_end": {
                                "type": "string",
                                "format": "date",
                                "example": "2026-09-30",
                                "description": "Last day of the billing period (YYYY-MM-DD)."
                              },
                              "issued_on": {
                                "type": "string",
                                "format": "date",
                                "example": "2026-10-01",
                                "description": "When the bill was issued (YYYY-MM-DD)."
                              },
                              "due_on": {
                                "type": "string",
                                "format": "date",
                                "example": "2026-10-15",
                                "description": "When the bill is due (YYYY-MM-DD)."
                              }
                            }
                          }
                        }
                      }
                    },
                    "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"
                      ]
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    },
                    "outstanding_minor": {
                      "type": "integer",
                      "format": "int64",
                      "example": 150000,
                      "description": "The bill's open amount in minor units as the provider stated it when the quote was issued (the supplier's quote, else its lookup; for a presented invoice, its open amount). Absent when the provider does not say. Fixed with the quote: a later read shows what the quote was issued with."
                    },
                    "allow_partial": {
                      "type": "boolean",
                      "example": false,
                      "description": "Whether the provider lets this quote pay less than `outstanding_minor` (for a presented invoice, the biller's partial-payment rule). Absent when the provider does not say."
                    },
                    "bill_details": {
                      "type": "object",
                      "description": "The bill as the provider described it on its quote, for display (IG-31): who holds it, where the service is, the billing period and the issue and due dates. Every field is optional; the object is absent when the provider says nothing. Display data, not terms: it is not part of the quote's digest and never stops a quote from being paid. Fixed with the quote: a read shows what it was issued with, a refresh what the provider says now. `holder_name` is personal data: masked for every reader (as `customer_name`), the owning channel included, and never in events or webhooks. Text is at most 200 characters without control characters; a date the provider sent that is not a calendar date is left out.",
                      "properties": {
                        "holder_name": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Ana Nhantumbo",
                          "description": "The bill's holder as the provider names them."
                        },
                        "service_address": {
                          "type": "string",
                          "maxLength": 200,
                          "example": "Av. 24 de Julho 1020, Maputo",
                          "description": "Where the service is supplied."
                        },
                        "period_start": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-01",
                          "description": "First day of the billing period (YYYY-MM-DD). Left out with `period_end` when it falls after it."
                        },
                        "period_end": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-09-30",
                          "description": "Last day of the billing period (YYYY-MM-DD)."
                        },
                        "issued_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-01",
                          "description": "When the bill was issued (YYYY-MM-DD)."
                        },
                        "due_on": {
                          "type": "string",
                          "format": "date",
                          "example": "2026-10-15",
                          "description": "When the bill is due (YYYY-MM-DD)."
                        }
                      }
                    }
                  }
                },
                "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"
                          },
                          "amount_minor": {
                            "type": "integer",
                            "description": "`amount` in minor units.",
                            "example": 25000
                          },
                          "payer_fee_minor": {
                            "type": "integer",
                            "description": "`payer_fee` in minor units.",
                            "example": 500
                          },
                          "total_minor": {
                            "type": "integer",
                            "description": "`total` in minor units.",
                            "example": 25500
                          },
                          "purchase_mode": {
                            "type": "string",
                            "enum": [
                              "fixed_catalog",
                              "variable_amount"
                            ],
                            "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                          },
                          "supplier_connector": {
                            "type": "string",
                            "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                          },
                          "refreshed_from": {
                            "type": "string",
                            "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                          },
                          "accepted_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "When the quote was accepted (paid or collected)."
                          },
                          "payment_status": {
                            "type": "string",
                            "enum": [
                              "none",
                              "collecting",
                              "reserved",
                              "captured",
                              "released",
                              "refunded",
                              "refund_pending",
                              "not_collected"
                            ],
                            "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                          },
                          "fulfilment": {
                            "type": "object",
                            "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                            "properties": {
                              "status": {
                                "type": "string",
                                "enum": [
                                  "not_started",
                                  "submitted",
                                  "unknown",
                                  "succeeded",
                                  "failed"
                                ]
                              },
                              "request_id": {
                                "type": "string"
                              },
                              "attempts": {
                                "type": "integer"
                              },
                              "supplier_reference": {
                                "type": "string"
                              },
                              "evidence": {
                                "type": "object",
                                "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                                "properties": {
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "fulfil_response",
                                      "status_query",
                                      "operator_resolution"
                                    ]
                                  },
                                  "authenticated_by": {
                                    "type": "string",
                                    "enum": [
                                      "connector_session",
                                      "dual_approval"
                                    ]
                                  },
                                  "outcome": {
                                    "type": "string",
                                    "enum": [
                                      "succeeded",
                                      "failed"
                                    ]
                                  },
                                  "connector_id": {
                                    "type": "string"
                                  },
                                  "driver": {
                                    "type": "string"
                                  },
                                  "request_id": {
                                    "type": "string"
                                  },
                                  "supplier_reference": {
                                    "type": "string"
                                  },
                                  "supplier_code": {
                                    "type": "string",
                                    "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                                  },
                                  "approval_id": {
                                    "type": "string"
                                  },
                                  "observed_at": {
                                    "type": "string",
                                    "format": "date-time"
                                  },
                                  "result_sha256": {
                                    "type": "string",
                                    "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "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"
                    },
                    "amount_minor": {
                      "type": "integer",
                      "description": "`amount` in minor units.",
                      "example": 25000
                    },
                    "payer_fee_minor": {
                      "type": "integer",
                      "description": "`payer_fee` in minor units.",
                      "example": 500
                    },
                    "total_minor": {
                      "type": "integer",
                      "description": "`total` in minor units.",
                      "example": 25500
                    },
                    "purchase_mode": {
                      "type": "string",
                      "enum": [
                        "fixed_catalog",
                        "variable_amount"
                      ],
                      "description": "`fixed_catalog`: an offer from the product's catalogue, at its price; `variable_amount`: an amount under the product's amount rule."
                    },
                    "supplier_connector": {
                      "type": "string",
                      "description": "The supply connector that issued the quote. The quote is paid through it or not at all."
                    },
                    "refreshed_from": {
                      "type": "string",
                      "description": "The quote this one refreshes (POST /v1/bill-payments/quotes/{id}/refresh); that quote is unchanged."
                    },
                    "accepted_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the quote was accepted (paid or collected)."
                    },
                    "payment_status": {
                      "type": "string",
                      "enum": [
                        "none",
                        "collecting",
                        "reserved",
                        "captured",
                        "released",
                        "refunded",
                        "refund_pending",
                        "not_collected"
                      ],
                      "description": "The money's state, independent of the fulfilment: the reserve from your balance (or the payer's collection) and what became of it."
                    },
                    "fulfilment": {
                      "type": "object",
                      "description": "The supplier leg, independent of the money: `not_started` (nothing sent), `submitted`, `unknown` (sent, no definitive answer: KulPay queries the same request id and never resolves it without evidence), `succeeded`, `failed`.",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "not_started",
                            "submitted",
                            "unknown",
                            "succeeded",
                            "failed"
                          ]
                        },
                        "request_id": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "supplier_reference": {
                          "type": "string"
                        },
                        "evidence": {
                          "type": "object",
                          "description": "What settled the fulfilment: the supplier's answer to the fulfilment request (`fulfil_response`) or to a status query (`status_query`), received over the issuing connector's own authenticated session (`connector_session`), or two operators' decision on a payment in review (`operator_resolution`, `dual_approval`). Absent while the outcome is unknown. Carries no token or PIN: `result_sha256` is a digest of the supplier's answer.",
                          "properties": {
                            "source": {
                              "type": "string",
                              "enum": [
                                "fulfil_response",
                                "status_query",
                                "operator_resolution"
                              ]
                            },
                            "authenticated_by": {
                              "type": "string",
                              "enum": [
                                "connector_session",
                                "dual_approval"
                              ]
                            },
                            "outcome": {
                              "type": "string",
                              "enum": [
                                "succeeded",
                                "failed"
                              ]
                            },
                            "connector_id": {
                              "type": "string"
                            },
                            "driver": {
                              "type": "string"
                            },
                            "request_id": {
                              "type": "string"
                            },
                            "supplier_reference": {
                              "type": "string"
                            },
                            "supplier_code": {
                              "type": "string",
                              "description": "The supplier's code when it answered with an error (e.g. `not_found` to a status query)."
                            },
                            "approval_id": {
                              "type": "string"
                            },
                            "observed_at": {
                              "type": "string",
                              "format": "date-time"
                            },
                            "result_sha256": {
                              "type": "string",
                              "example": "5c1f0e6b2d9a4c7e8f3a1b2c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f70"
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "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": "anaexample.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`. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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"
                      },
                      "payment_reference": {
                        "type": "string",
                        "description": "The ISO 11649 reference (RF\u2026, grouped by four) a payer quotes to pay at a partner bank or PSP counter or by transfer (BIL-5.8)."
                      },
                      "pay_url": {
                        "type": "string",
                        "description": "The invoice's payment link, while it takes payments (BIL-5.9); reissue it with POST /v1/biller/invoices/{number}/payment-link."
                      },
                      "qr": {
                        "type": "object",
                        "description": "Its QR while it takes payments (BIL-5.12), on a read of one invoice: `url` is the invoice's payment link, which any phone camera opens; render it, or use the rendered images.",
                        "properties": {
                          "url": {
                            "type": "string"
                          },
                          "svg_url": {
                            "type": "string"
                          },
                          "png_url": {
                            "type": "string"
                          }
                        }
                      },
                      "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"
                            },
                            "payment_reference": {
                              "type": "string",
                              "description": "The ISO 11649 reference (RF\u2026, grouped by four) a payer quotes to pay at a partner bank or PSP counter or by transfer (BIL-5.8)."
                            },
                            "pay_url": {
                              "type": "string",
                              "description": "The invoice's payment link, while it takes payments (BIL-5.9); reissue it with POST /v1/biller/invoices/{number}/payment-link."
                            },
                            "qr": {
                              "type": "object",
                              "description": "Its QR while it takes payments (BIL-5.12), on a read of one invoice: `url` is the invoice's payment link, which any phone camera opens; render it, or use the rendered images.",
                              "properties": {
                                "url": {
                                  "type": "string"
                                },
                                "svg_url": {
                                  "type": "string"
                                },
                                "png_url": {
                                  "type": "string"
                                }
                              }
                            },
                            "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}/qr.svg": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillerInvoiceQR",
        "summary": "An invoice's QR (biller)",
        "description": "The QR to print on your invoice (BIL-5.12): it encodes the invoice's payment link (`qr.url`), a plain https link any phone camera opens; the page shows the invoice and takes the payment, and a partner's app that reads it pays it in the app. No personal data. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No such 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`: a paid, cancelled or expired invoice has no QR.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice F-Q takes no payments, so it has no QR",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice F-Q takes no payments, so it has no QR"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices/{number}/qr.png": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillerInvoiceQRPNG",
        "summary": "An invoice's QR as PNG (biller)",
        "description": "The QR to print on your invoice (BIL-5.12): it encodes the invoice's payment link (`qr.url`), a plain https link any phone camera opens; the page shows the invoice and takes the payment, and a partner's app that reads it pays it in the app. No personal data. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No such 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`: a paid, cancelled or expired invoice has no QR.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice F-Q takes no payments, so it has no QR",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice F-Q takes no payments, so it has no QR"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/accounts/{ref}/qr": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillerAccountQR",
        "summary": "A customer account's QR (biller)",
        "description": "A QR for one of your customer accounts (BIL-5.13): print it once (on the contract, a bill, a sticker) and your customer pays every month. It encodes a plain https link any phone camera opens; the page shows what is due on the account now (its open invoices, oldest due first), each paid on its invoice page. A partner's app that reads it resolves it (`POST /v1/qr/resolve`) and pays in the app. In the mode of your key. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The account's QR.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "biller": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string",
                      "description": "The mode its payments run in: the key's."
                    },
                    "url": {
                      "type": "string",
                      "description": "What the QR encodes: the account's link (/pay/account/{token}), which any phone camera opens. The token names the biller, product and account under an HMAC; it is the same every time."
                    },
                    "svg_url": {
                      "type": "string"
                    },
                    "png_url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "biller": "AGUAS",
                  "reference": "CL-001234",
                  "mode": "live",
                  "url": "https://pay.kulpay.co.mz/pay/account/bB9BR1VBUx8fQ0wtMDA0Mg.3oT2Lw9sQe1Zk7mN",
                  "svg_url": "/v1/biller/accounts/CL-001234/qr.svg",
                  "png_url": "/v1/biller/accounts/CL-001234/qr.png"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/biller/accounts/{ref}/qr.svg": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillerAccountQRSVG",
        "summary": "A customer account's QR, rendered (biller)",
        "description": "A QR for one of your customer accounts (BIL-5.13): print it once (on the contract, a bill, a sticker) and your customer pays every month. It encodes a plain https link any phone camera opens; the page shows what is due on the account now (its open invoices, oldest due first), each paid on its invoice page. A partner's app that reads it resolves it (`POST /v1/qr/resolve`) and pays in the app. In the mode of your key. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/biller/accounts/{ref}/qr.png": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getBillerAccountQRPNG",
        "summary": "A customer account's QR, PNG (biller)",
        "description": "A QR for one of your customer accounts (BIL-5.13): print it once (on the contract, a bill, a sticker) and your customer pays every month. It encodes a plain https link any phone camera opens; the page shows what is due on the account now (its open invoices, oldest due first), each paid on its invoice page. A partner's app that reads it resolves it (`POST /v1/qr/resolve`) and pays in the app. In the mode of your key. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/admin/billers/{code}/accounts/{reference}/qr": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminAccountQR",
        "summary": "A meter's or account's QR (operators)",
        "description": "A QR for a meter or customer account of a biller with no portal (BIL-5.13): a Credelec meter, an airtime number. Its page buys for the meter through the hosted checkout (amount and the payer's phone, which gets the token by SMS). As `GET /v1/biller/accounts/{reference}/qr`. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The account's QR.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "biller": {
                      "type": "string"
                    },
                    "product": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string",
                      "description": "The mode its payments run in: the key's."
                    },
                    "url": {
                      "type": "string",
                      "description": "What the QR encodes: the account's link (/pay/account/{token}), which any phone camera opens. The token names the biller, product and account under an HMAC; it is the same every time."
                    },
                    "svg_url": {
                      "type": "string"
                    },
                    "png_url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "biller": "AGUAS",
                  "reference": "CL-0042",
                  "mode": "live",
                  "url": "https://pay.kulpay.co.mz/pay/account/bB9BR1VBUx8fQ0wtMDA0Mg.3oT2Lw9sQe1Zk7mN",
                  "svg_url": "/v1/biller/accounts/CL-001234/qr.svg",
                  "png_url": "/v1/biller/accounts/CL-001234/qr.png"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/admin/billers/{code}/accounts/{reference}/qr.svg": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminAccountQRSVG",
        "summary": "A meter's or account's QR, rendered (operators)",
        "description": "A QR for a meter or customer account of a biller with no portal (BIL-5.13): a Credelec meter, an airtime number. Its page buys for the meter through the hosted checkout (amount and the payer's phone, which gets the token by SMS). As `GET /v1/biller/accounts/{reference}/qr`. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/admin/billers/{code}/accounts/{reference}/qr.png": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminAccountQRPNG",
        "summary": "A meter's or account's QR, PNG (operators)",
        "description": "A QR for a meter or customer account of a biller with no portal (BIL-5.13): a Credelec meter, an airtime number. Its page buys for the meter through the hosted checkout (amount and the payer's phone, which gets the token by SMS). As `GET /v1/biller/accounts/{reference}/qr`. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The customer account or meter number: 1 to 40 printable characters, no spaces."
          },
          {
            "name": "product",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The product, when the biller has more than one that takes this reference (a meter: CREDELEC). The reference must be valid for it."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`: the reference is not valid for the biller's products (or the product named), or the biller is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no product of AGUAS takes CL-****2 as a reference",
                  "error": {
                    "code": "invalid_request",
                    "message": "no product of AGUAS takes CL-****2 as a reference"
                  }
                }
              }
            }
          },
          "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/biller/products": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerProducts",
        "summary": "Your products (biller)",
        "description": "Your products as KulPay sells them, with what you integrate against (BIL-5.15): `supplier_product`, the code your endpoints receive as `product` (the one you gave, else the product's own code; never empty), the reference each takes, whether lookups are routed to it, the fee you bear, and the connectors (endpoints) serving it. Requires `invoices:read`.",
        "responses": {
          "200": {
            "description": "Your products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": [
                    {
                      "code": "CONTA",
                      "name": "Conta de \u00e1gua",
                      "shape": "postpaid",
                      "currency": "MZN",
                      "amount_rule": "range",
                      "min": "1.00",
                      "max": "50000.00",
                      "customer_reference": {
                        "label": "Contrato",
                        "pattern": "^EX-\\d{6}$",
                        "example": "EX-123456"
                      },
                      "lookup_required": true,
                      "fee": {
                        "bearer": "biller",
                        "currency": "MZN"
                      },
                      "purchase_modes": [
                        "variable_amount"
                      ],
                      "version": 2,
                      "enabled": true,
                      "routable": true,
                      "model": "agent",
                      "supplier_product": "AGUA_DOM",
                      "biller_fee": {
                        "bearer": "biller",
                        "fixed": "5.00",
                        "currency": "MZN"
                      },
                      "connectors": [
                        {
                          "name": "aguas-live",
                          "driver": "biller_rest",
                          "mode": "live",
                          "health": "healthy"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Not a biller's key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "not_a_biller",
                  "detail": "this key is not a biller's",
                  "error": {
                    "code": "not_a_biller",
                    "message": "this key is not a biller's"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/products/{product}/integration": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerGetIntegration",
        "summary": "A product's notification and report (biller)",
        "description": "The product's own integration with its LSP in a mode (BIL-5.16), secrets masked; defaults when none was set. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          }
        ],
        "responses": {
          "200": {
            "description": "The integration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "biller": "AGUAS",
                  "product": "FACTURA",
                  "mode": "test",
                  "version": 3,
                  "notification": {
                    "enabled": true,
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                    "headers": {
                      "X-Biller-Code": "AGUAS-01"
                    },
                    "auth": "hmac"
                  },
                  "report": {
                    "enabled": true,
                    "format": "txt",
                    "columns": [
                      {
                        "field": "paid_at",
                        "header": "DATA",
                        "width": 10
                      },
                      {
                        "field": "invoice_number",
                        "header": "FACTURA",
                        "width": 12
                      },
                      {
                        "field": "total",
                        "header": "VALOR",
                        "width": 10,
                        "align": "right"
                      }
                    ],
                    "header": true,
                    "date_format": "DD/MM/YYYY",
                    "decimal_separator": ",",
                    "line_ending": "lf",
                    "file_name": "AGUAS_{date}.dat",
                    "url": "https://lsp.example/kulpay/reconciliacao",
                    "auth": "bearer",
                    "last_day": "2026-10-19"
                  },
                  "fields": [
                    "event",
                    "notification_id",
                    "payment_id",
                    "request_id",
                    "mode",
                    "biller",
                    "biller_name",
                    "product",
                    "supplier_product",
                    "reference",
                    "invoice_number",
                    "amount",
                    "payer_fee",
                    "total",
                    "currency",
                    "amount_minor",
                    "total_minor",
                    "paid_at",
                    "channel_id",
                    "channel_name",
                    "funding",
                    "partner_reference",
                    "supplier_reference",
                    "receipt_url"
                  ],
                  "updated_at": "2026-10-20T08:00:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerPutIntegration",
        "summary": "Set a product's notification and report (biller)",
        "description": "Sets the payment notification (URL, body template, content type, auth, headers) and the daily report (format, columns, file name, URL) of a product in a mode (BIL-5.16). Fields left out keep their value. Each change is a new version (bill_product.integration_set). The change takes effect at once, in live too, with no second approval (owner decision 2026-10-20). Notification and report URLs follow the webhook URL rules: absolute https, no credentials, at most 500 characters, never a link-local, multicast or reserved address. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notification": {
                    "type": "object",
                    "description": "The payment notification: every payment of the product that succeeds is sent to `url`, its body rendered from `template` (Go text/template over the fields; helpers `json`, `xml`, `url` escape a value, `date \"DD/MM/YYYY HH:mm\"` formats paid_at, `cents` drops the decimal point). Checked when set: the body must be what `content_type` says. Retried for 24 h; kept for resends.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "method": {
                        "type": "string",
                        "enum": [
                          "POST",
                          "PUT"
                        ]
                      },
                      "content_type": {
                        "type": "string",
                        "enum": [
                          "application/json",
                          "application/xml",
                          "application/x-www-form-urlencoded",
                          "text/plain"
                        ]
                      },
                      "template": {
                        "type": "string"
                      },
                      "headers": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        },
                        "description": "Up to 10 static headers; not Authorization, Content-Type or KulPay-*."
                      },
                      "auth": {
                        "type": "object",
                        "description": "How KulPay authenticates at the endpoint. `hmac` signs the body: KulPay-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"t.body\">. Secrets are write-only.",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "none",
                              "bearer",
                              "basic",
                              "hmac"
                            ]
                          },
                          "token": {
                            "type": "string"
                          },
                          "username": {
                            "type": "string"
                          },
                          "password": {
                            "type": "string"
                          },
                          "secret": {
                            "type": "string",
                            "minLength": 16
                          }
                        }
                      }
                    }
                  },
                  "report": {
                    "type": "object",
                    "description": "The daily report: the product's payments of a day, in the LSP's format and file extension; downloadable, and sent to `url` the next day.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "format": {
                        "type": "string",
                        "enum": [
                          "csv",
                          "tsv",
                          "psv",
                          "txt",
                          "json",
                          "xml",
                          "xlsx"
                        ]
                      },
                      "columns": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "field": {
                              "type": "string",
                              "enum": [
                                "event",
                                "notification_id",
                                "payment_id",
                                "request_id",
                                "mode",
                                "biller",
                                "biller_name",
                                "product",
                                "supplier_product",
                                "reference",
                                "invoice_number",
                                "amount",
                                "payer_fee",
                                "total",
                                "currency",
                                "amount_minor",
                                "total_minor",
                                "paid_at",
                                "channel_id",
                                "channel_name",
                                "funding",
                                "partner_reference",
                                "supplier_reference",
                                "receipt_url"
                              ]
                            },
                            "header": {
                              "type": "string"
                            },
                            "width": {
                              "type": "integer",
                              "description": "txt (fixed width) only."
                            },
                            "align": {
                              "type": "string",
                              "enum": [
                                "left",
                                "right"
                              ]
                            }
                          },
                          "required": [
                            "field"
                          ]
                        }
                      },
                      "header": {
                        "type": "boolean"
                      },
                      "date_format": {
                        "type": "string",
                        "example": "DD/MM/YYYY HH:mm"
                      },
                      "decimal_separator": {
                        "type": "string",
                        "enum": [
                          ".",
                          ","
                        ]
                      },
                      "line_ending": {
                        "type": "string",
                        "enum": [
                          "lf",
                          "crlf"
                        ]
                      },
                      "file_name": {
                        "type": "string",
                        "description": "Placeholders {biller}, {product}, {date}, {mode}; the extension is the one given, else the format's.",
                        "example": "AGUAS_{date}.dat"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "auth": {
                        "type": "object",
                        "description": "How KulPay authenticates at the endpoint. `hmac` signs the body: KulPay-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"t.body\">. Secrets are write-only.",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "none",
                              "bearer",
                              "basic",
                              "hmac"
                            ]
                          },
                          "token": {
                            "type": "string"
                          },
                          "username": {
                            "type": "string"
                          },
                          "password": {
                            "type": "string"
                          },
                          "secret": {
                            "type": "string",
                            "minLength": 16
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "notification": {
                  "enabled": true,
                  "url": "https://lsp.example/kulpay/pagamentos",
                  "content_type": "application/xml",
                  "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                  "headers": {
                    "X-Biller-Code": "AGUAS-01"
                  },
                  "auth": {
                    "type": "hmac",
                    "secret": "<o seu segredo>"
                  }
                },
                "report": {
                  "enabled": true,
                  "format": "txt",
                  "decimal_separator": ",",
                  "date_format": "DD/MM/YYYY",
                  "file_name": "AGUAS_{date}.dat",
                  "columns": [
                    {
                      "field": "paid_at",
                      "header": "DATA",
                      "width": 10
                    },
                    {
                      "field": "invoice_number",
                      "header": "FACTURA",
                      "width": 12
                    },
                    {
                      "field": "total",
                      "header": "VALOR",
                      "width": 10,
                      "align": "right"
                    }
                  ],
                  "url": "https://lsp.example/kulpay/reconciliacao",
                  "auth": {
                    "type": "bearer",
                    "token": "<o seu token>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The integration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "biller": "AGUAS",
                  "product": "FACTURA",
                  "mode": "test",
                  "version": 3,
                  "notification": {
                    "enabled": true,
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                    "headers": {
                      "X-Biller-Code": "AGUAS-01"
                    },
                    "auth": "hmac"
                  },
                  "report": {
                    "enabled": true,
                    "format": "txt",
                    "columns": [
                      {
                        "field": "paid_at",
                        "header": "DATA",
                        "width": 10
                      },
                      {
                        "field": "invoice_number",
                        "header": "FACTURA",
                        "width": 12
                      },
                      {
                        "field": "total",
                        "header": "VALOR",
                        "width": 10,
                        "align": "right"
                      }
                    ],
                    "header": true,
                    "date_format": "DD/MM/YYYY",
                    "decimal_separator": ",",
                    "line_ending": "lf",
                    "file_name": "AGUAS_{date}.dat",
                    "url": "https://lsp.example/kulpay/reconciliacao",
                    "auth": "bearer",
                    "last_day": "2026-10-19"
                  },
                  "fields": [
                    "event",
                    "notification_id",
                    "payment_id",
                    "request_id",
                    "mode",
                    "biller",
                    "biller_name",
                    "product",
                    "supplier_product",
                    "reference",
                    "invoice_number",
                    "amount",
                    "payer_fee",
                    "total",
                    "currency",
                    "amount_minor",
                    "total_minor",
                    "paid_at",
                    "channel_id",
                    "channel_name",
                    "funding",
                    "partner_reference",
                    "supplier_reference",
                    "receipt_url"
                  ],
                  "updated_at": "2026-10-20T08:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "notification: the template does not render well-formed XML (use {{xml .field}} for values)",
                  "error": {
                    "code": "invalid_request",
                    "message": "notification: the template does not render well-formed XML (use {{xml .field}} for values)"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/biller/products/{product}/integration/preview": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerPreviewIntegration",
        "summary": "Preview a notification's body (biller)",
        "description": "Renders a template (the one given, else the product's) for a sample payment, as the LSP would receive it. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "template": {
                    "type": "string"
                  },
                  "content_type": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                "content_type": "application/xml"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rendered body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "content_type": "application/xml",
                  "body": "<pagamento><referencia>EX-123456</referencia><valor>126000</valor><data>19/10/2026 16:30</data></pagamento>"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "template: unexpected \"}\" in operand",
                  "error": {
                    "code": "invalid_request",
                    "message": "template: unexpected \"}\" in operand"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/biller/products/{product}/integration/deliveries": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerListIntegrationDeliveries",
        "summary": "A product's notifications and reports sent (biller)",
        "description": "The deliveries to the LSP, newest first: status pending (retrying), delivered or exhausted, attempts, the endpoint's last answer. Requires `invoices:read`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "notification",
                "report"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": [
                    {
                      "id": "3KvxDelivery00000000000001",
                      "integration_id": "3KvxIntegration0000000001",
                      "kind": "notification",
                      "subject_id": "3KvxPayment000000000000001",
                      "mode": "test",
                      "url": "https://lsp.example/kulpay/pagamentos",
                      "method": "POST",
                      "content_type": "application/xml",
                      "status": "delivered",
                      "attempts": 1,
                      "next_attempt_at": "2026-10-20T08:00:00Z",
                      "response_code": 200,
                      "duration_ms": 84,
                      "created_at": "2026-10-20T08:00:00Z",
                      "updated_at": "2026-10-20T08:00:01Z",
                      "delivered_at": "2026-10-20T08:00:01Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/biller/products/{product}/integration/deliveries/{id}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerGetIntegrationDelivery",
        "summary": "One delivery and its exact body (biller)",
        "description": "What was sent, byte for byte: `body` (text) or `body_base64` (xlsx). Requires `invoices:read`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The delivery id."
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "delivery": {
                    "id": "3KvxDelivery00000000000001",
                    "integration_id": "3KvxIntegration0000000001",
                    "kind": "notification",
                    "subject_id": "3KvxPayment000000000000001",
                    "mode": "test",
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "status": "delivered",
                    "attempts": 1,
                    "next_attempt_at": "2026-10-20T08:00:00Z",
                    "response_code": 200,
                    "duration_ms": 84,
                    "created_at": "2026-10-20T08:00:00Z",
                    "updated_at": "2026-10-20T08:00:01Z",
                    "delivered_at": "2026-10-20T08:00:01Z"
                  },
                  "body": "<pagamento><referencia>CL-1</referencia><valor>151000</valor><data>19/10/2026 12:00</data></pagamento>"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/biller/products/{product}/integration/deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerRetryIntegrationDelivery",
        "summary": "Send a delivery again (biller)",
        "description": "Sends a pending or exhausted delivery again now, the same body. Requires `webhooks:manage`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The delivery id."
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery after the attempt.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "delivery": {
                    "id": "3KvxDelivery00000000000001",
                    "integration_id": "3KvxIntegration0000000001",
                    "kind": "notification",
                    "subject_id": "3KvxPayment000000000000001",
                    "mode": "test",
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "status": "delivered",
                    "attempts": 1,
                    "next_attempt_at": "2026-10-20T08:00:00Z",
                    "response_code": 200,
                    "duration_ms": 84,
                    "created_at": "2026-10-20T08:00:00Z",
                    "updated_at": "2026-10-20T08:00:01Z",
                    "delivered_at": "2026-10-20T08:00:01Z"
                  },
                  "body": "<pagamento>\u2026</pagamento>"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/biller/products/{product}/reports/{day}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "billerProductReport",
        "summary": "A product's report for a day (biller)",
        "description": "The product's payments that succeeded on the day (Maputo), in its report format and file name (Content-Disposition). Requires `invoices:read`.",
        "parameters": [
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "day",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "The day, YYYY-MM-DD, Maputo time."
          }
        ],
        "responses": {
          "200": {
            "description": "The file, in the product's format.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                },
                "example": "DATA      FACTURA          VALOR\n19/10/2026F-N1           1510,00\n"
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the day is YYYY-MM-DD",
                  "error": {
                    "code": "invalid_request",
                    "message": "the day is YYYY-MM-DD"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/integration": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminGetIntegration",
        "summary": "A product's notification and report (admin)",
        "description": "The product's own integration with its LSP in a mode (BIL-5.16), secrets masked; defaults when none was set. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          }
        ],
        "responses": {
          "200": {
            "description": "The integration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "biller": "AGUAS",
                  "product": "FACTURA",
                  "mode": "test",
                  "version": 3,
                  "notification": {
                    "enabled": true,
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                    "headers": {
                      "X-Biller-Code": "AGUAS-01"
                    },
                    "auth": "hmac"
                  },
                  "report": {
                    "enabled": true,
                    "format": "txt",
                    "columns": [
                      {
                        "field": "paid_at",
                        "header": "DATA",
                        "width": 10
                      },
                      {
                        "field": "invoice_number",
                        "header": "FACTURA",
                        "width": 12
                      },
                      {
                        "field": "total",
                        "header": "VALOR",
                        "width": 10,
                        "align": "right"
                      }
                    ],
                    "header": true,
                    "date_format": "DD/MM/YYYY",
                    "decimal_separator": ",",
                    "line_ending": "lf",
                    "file_name": "AGUAS_{date}.dat",
                    "url": "https://lsp.example/kulpay/reconciliacao",
                    "auth": "bearer",
                    "last_day": "2026-10-19"
                  },
                  "fields": [
                    "event",
                    "notification_id",
                    "payment_id",
                    "request_id",
                    "mode",
                    "biller",
                    "biller_name",
                    "product",
                    "supplier_product",
                    "reference",
                    "invoice_number",
                    "amount",
                    "payer_fee",
                    "total",
                    "currency",
                    "amount_minor",
                    "total_minor",
                    "paid_at",
                    "channel_id",
                    "channel_name",
                    "funding",
                    "partner_reference",
                    "supplier_reference",
                    "receipt_url"
                  ],
                  "updated_at": "2026-10-20T08:00:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminPutIntegration",
        "summary": "Set a product's notification and report (admin)",
        "description": "Sets the payment notification (URL, body template, content type, auth, headers) and the daily report (format, columns, file name, URL) of a product in a mode (BIL-5.16). Fields left out keep their value. Each change is a new version (bill_product.integration_set). The change takes effect at once, in live too, with no second approval (owner decision 2026-10-20). Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notification": {
                    "type": "object",
                    "description": "The payment notification: every payment of the product that succeeds is sent to `url`, its body rendered from `template` (Go text/template over the fields; helpers `json`, `xml`, `url` escape a value, `date \"DD/MM/YYYY HH:mm\"` formats paid_at, `cents` drops the decimal point). Checked when set: the body must be what `content_type` says. Retried for 24 h; kept for resends.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "method": {
                        "type": "string",
                        "enum": [
                          "POST",
                          "PUT"
                        ]
                      },
                      "content_type": {
                        "type": "string",
                        "enum": [
                          "application/json",
                          "application/xml",
                          "application/x-www-form-urlencoded",
                          "text/plain"
                        ]
                      },
                      "template": {
                        "type": "string"
                      },
                      "headers": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        },
                        "description": "Up to 10 static headers; not Authorization, Content-Type or KulPay-*."
                      },
                      "auth": {
                        "type": "object",
                        "description": "How KulPay authenticates at the endpoint. `hmac` signs the body: KulPay-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"t.body\">. Secrets are write-only.",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "none",
                              "bearer",
                              "basic",
                              "hmac"
                            ]
                          },
                          "token": {
                            "type": "string"
                          },
                          "username": {
                            "type": "string"
                          },
                          "password": {
                            "type": "string"
                          },
                          "secret": {
                            "type": "string",
                            "minLength": 16
                          }
                        }
                      }
                    }
                  },
                  "report": {
                    "type": "object",
                    "description": "The daily report: the product's payments of a day, in the LSP's format and file extension; downloadable, and sent to `url` the next day.",
                    "properties": {
                      "enabled": {
                        "type": "boolean"
                      },
                      "format": {
                        "type": "string",
                        "enum": [
                          "csv",
                          "tsv",
                          "psv",
                          "txt",
                          "json",
                          "xml",
                          "xlsx"
                        ]
                      },
                      "columns": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "field": {
                              "type": "string",
                              "enum": [
                                "event",
                                "notification_id",
                                "payment_id",
                                "request_id",
                                "mode",
                                "biller",
                                "biller_name",
                                "product",
                                "supplier_product",
                                "reference",
                                "invoice_number",
                                "amount",
                                "payer_fee",
                                "total",
                                "currency",
                                "amount_minor",
                                "total_minor",
                                "paid_at",
                                "channel_id",
                                "channel_name",
                                "funding",
                                "partner_reference",
                                "supplier_reference",
                                "receipt_url"
                              ]
                            },
                            "header": {
                              "type": "string"
                            },
                            "width": {
                              "type": "integer",
                              "description": "txt (fixed width) only."
                            },
                            "align": {
                              "type": "string",
                              "enum": [
                                "left",
                                "right"
                              ]
                            }
                          },
                          "required": [
                            "field"
                          ]
                        }
                      },
                      "header": {
                        "type": "boolean"
                      },
                      "date_format": {
                        "type": "string",
                        "example": "DD/MM/YYYY HH:mm"
                      },
                      "decimal_separator": {
                        "type": "string",
                        "enum": [
                          ".",
                          ","
                        ]
                      },
                      "line_ending": {
                        "type": "string",
                        "enum": [
                          "lf",
                          "crlf"
                        ]
                      },
                      "file_name": {
                        "type": "string",
                        "description": "Placeholders {biller}, {product}, {date}, {mode}; the extension is the one given, else the format's.",
                        "example": "AGUAS_{date}.dat"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "auth": {
                        "type": "object",
                        "description": "How KulPay authenticates at the endpoint. `hmac` signs the body: KulPay-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"t.body\">. Secrets are write-only.",
                        "properties": {
                          "type": {
                            "type": "string",
                            "enum": [
                              "none",
                              "bearer",
                              "basic",
                              "hmac"
                            ]
                          },
                          "token": {
                            "type": "string"
                          },
                          "username": {
                            "type": "string"
                          },
                          "password": {
                            "type": "string"
                          },
                          "secret": {
                            "type": "string",
                            "minLength": 16
                          }
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "notification": {
                  "enabled": true,
                  "url": "https://lsp.example/kulpay/pagamentos",
                  "content_type": "application/xml",
                  "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                  "headers": {
                    "X-Biller-Code": "AGUAS-01"
                  },
                  "auth": {
                    "type": "hmac",
                    "secret": "<o seu segredo>"
                  }
                },
                "report": {
                  "enabled": true,
                  "format": "txt",
                  "decimal_separator": ",",
                  "date_format": "DD/MM/YYYY",
                  "file_name": "AGUAS_{date}.dat",
                  "columns": [
                    {
                      "field": "paid_at",
                      "header": "DATA",
                      "width": 10
                    },
                    {
                      "field": "invoice_number",
                      "header": "FACTURA",
                      "width": 12
                    },
                    {
                      "field": "total",
                      "header": "VALOR",
                      "width": 10,
                      "align": "right"
                    }
                  ],
                  "url": "https://lsp.example/kulpay/reconciliacao",
                  "auth": {
                    "type": "bearer",
                    "token": "<o seu token>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The integration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "biller": "AGUAS",
                  "product": "FACTURA",
                  "mode": "test",
                  "version": 3,
                  "notification": {
                    "enabled": true,
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                    "headers": {
                      "X-Biller-Code": "AGUAS-01"
                    },
                    "auth": "hmac"
                  },
                  "report": {
                    "enabled": true,
                    "format": "txt",
                    "columns": [
                      {
                        "field": "paid_at",
                        "header": "DATA",
                        "width": 10
                      },
                      {
                        "field": "invoice_number",
                        "header": "FACTURA",
                        "width": 12
                      },
                      {
                        "field": "total",
                        "header": "VALOR",
                        "width": 10,
                        "align": "right"
                      }
                    ],
                    "header": true,
                    "date_format": "DD/MM/YYYY",
                    "decimal_separator": ",",
                    "line_ending": "lf",
                    "file_name": "AGUAS_{date}.dat",
                    "url": "https://lsp.example/kulpay/reconciliacao",
                    "auth": "bearer",
                    "last_day": "2026-10-19"
                  },
                  "fields": [
                    "event",
                    "notification_id",
                    "payment_id",
                    "request_id",
                    "mode",
                    "biller",
                    "biller_name",
                    "product",
                    "supplier_product",
                    "reference",
                    "invoice_number",
                    "amount",
                    "payer_fee",
                    "total",
                    "currency",
                    "amount_minor",
                    "total_minor",
                    "paid_at",
                    "channel_id",
                    "channel_name",
                    "funding",
                    "partner_reference",
                    "supplier_reference",
                    "receipt_url"
                  ],
                  "updated_at": "2026-10-20T08:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "notification: the template does not render well-formed XML (use {{xml .field}} for values)",
                  "error": {
                    "code": "invalid_request",
                    "message": "notification: the template does not render well-formed XML (use {{xml .field}} for values)"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/integration/preview": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminPreviewIntegration",
        "summary": "Preview a notification's body (admin)",
        "description": "Renders a template (the one given, else the product's) for a sample payment, as the LSP would receive it. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "template": {
                    "type": "string"
                  },
                  "content_type": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "template": "<pagamento><referencia>{{xml .reference}}</referencia><valor>{{cents .total}}</valor><data>{{date \"DD/MM/YYYY HH:mm\" .paid_at}}</data></pagamento>",
                "content_type": "application/xml"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The rendered body.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "content_type": "application/xml",
                  "body": "<pagamento><referencia>EX-123456</referencia><valor>126000</valor><data>19/10/2026 16:30</data></pagamento>"
                }
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "template: unexpected \"}\" in operand",
                  "error": {
                    "code": "invalid_request",
                    "message": "template: unexpected \"}\" in operand"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/integration/deliveries": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminListIntegrationDeliveries",
        "summary": "A product's notifications and reports sent (admin)",
        "description": "The deliveries to the LSP, newest first: status pending (retrying), delivered or exhausted, attempts, the endpoint's last answer. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "notification",
                "report"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "data": [
                    {
                      "id": "3KvxDelivery00000000000001",
                      "integration_id": "3KvxIntegration0000000001",
                      "kind": "notification",
                      "subject_id": "3KvxPayment000000000000001",
                      "mode": "test",
                      "url": "https://lsp.example/kulpay/pagamentos",
                      "method": "POST",
                      "content_type": "application/xml",
                      "status": "delivered",
                      "attempts": 1,
                      "next_attempt_at": "2026-10-20T08:00:00Z",
                      "response_code": 200,
                      "duration_ms": 84,
                      "created_at": "2026-10-20T08:00:00Z",
                      "updated_at": "2026-10-20T08:00:01Z",
                      "delivered_at": "2026-10-20T08:00:01Z"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/integration/deliveries/{id}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminGetIntegrationDelivery",
        "summary": "One delivery and its exact body (admin)",
        "description": "What was sent, byte for byte: `body` (text) or `body_base64` (xlsx). Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The delivery id."
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "delivery": {
                    "id": "3KvxDelivery00000000000001",
                    "integration_id": "3KvxIntegration0000000001",
                    "kind": "notification",
                    "subject_id": "3KvxPayment000000000000001",
                    "mode": "test",
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "status": "delivered",
                    "attempts": 1,
                    "next_attempt_at": "2026-10-20T08:00:00Z",
                    "response_code": 200,
                    "duration_ms": 84,
                    "created_at": "2026-10-20T08:00:00Z",
                    "updated_at": "2026-10-20T08:00:01Z",
                    "delivered_at": "2026-10-20T08:00:01Z"
                  },
                  "body": "<pagamento><referencia>CL-1</referencia><valor>151000</valor><data>19/10/2026 12:00</data></pagamento>"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/integration/deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminRetryIntegrationDelivery",
        "summary": "Send a delivery again (admin)",
        "description": "Sends a pending or exhausted delivery again now, the same body. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The delivery id."
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery after the attempt.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "delivery": {
                    "id": "3KvxDelivery00000000000001",
                    "integration_id": "3KvxIntegration0000000001",
                    "kind": "notification",
                    "subject_id": "3KvxPayment000000000000001",
                    "mode": "test",
                    "url": "https://lsp.example/kulpay/pagamentos",
                    "method": "POST",
                    "content_type": "application/xml",
                    "status": "delivered",
                    "attempts": 1,
                    "next_attempt_at": "2026-10-20T08:00:00Z",
                    "response_code": 200,
                    "duration_ms": 84,
                    "created_at": "2026-10-20T08:00:00Z",
                    "updated_at": "2026-10-20T08:00:01Z",
                    "delivered_at": "2026-10-20T08:00:01Z"
                  },
                  "body": "<pagamento>\u2026</pagamento>"
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/billers/{code}/products/{product}/reports/{day}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "adminProductReport",
        "summary": "A product's report for a day (admin)",
        "description": "The product's payments that succeeded on the day (Maputo), in its report format and file name (Content-Disposition). Requires `connectors:read`.",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller code."
          },
          {
            "name": "product",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The product code (KulPay's)."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "The mode whose integration (the LSP's test and live endpoints differ). Default: the key's."
          },
          {
            "name": "day",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "The day, YYYY-MM-DD, Maputo time."
          }
        ],
        "responses": {
          "200": {
            "description": "The file, in the product's format.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                },
                "example": "DATA      FACTURA          VALOR\n19/10/2026F-N1           1510,00\n"
              }
            }
          },
          "400": {
            "description": "`invalid_request`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the day is YYYY-MM-DD",
                  "error": {
                    "code": "invalid_request",
                    "message": "the day is YYYY-MM-DD"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such biller or 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/bill-invoices/{id}/qr.svg": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getChannelInvoiceQR",
        "summary": "A bill's QR (channel)",
        "description": "The QR of a bill you may pay, to show your customer (BIL-5.12): it encodes the invoice's payment link (`qr.url`), which any phone camera opens. No personal data. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice id."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No such invoice, or one you may not pay.",
            "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`: a paid, cancelled or expired invoice has no QR.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice F-Q takes no payments, so it has no QR",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice F-Q takes no payments, so it has no QR"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/bill-invoices/{id}/qr.png": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getChannelInvoiceQRPNG",
        "summary": "A bill's QR as PNG (channel)",
        "description": "The QR of a bill you may pay, to show your customer (BIL-5.12): it encodes the invoice's payment link (`qr.url`), which any phone camera opens. No personal data. Requires `bills:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The invoice id."
          }
        ],
        "responses": {
          "200": {
            "description": "The QR: image/svg+xml for qr.svg, image/png for qr.png (512 px). Error correction M, with the quiet zone.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "404": {
            "description": "No such invoice, or one you may not pay.",
            "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`: a paid, cancelled or expired invoice has no QR.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice F-Q takes no payments, so it has no QR",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice F-Q takes no payments, so it has no QR"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pay/invoice/{token}/qr.svg": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getInvoicePaymentPageQR",
        "summary": "The payment page's QR",
        "security": [],
        "description": "Public, as the payment page: the invoice's QR as SVG, for a payer who opened the page on a computer and pays with the app on their phone (BIL-5.12).",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The QR.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such link, or the invoice takes no payments.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/biller/invoices/{number}/payment-link": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "reissueInvoicePaymentLink",
        "summary": "Reissue an invoice's payment link",
        "description": "Replaces the invoice's payment link (BIL-5.9): the old link stops working at once. Every invoice gets a link when presented (`pay_url`, while it takes payments) and an ISO 11649 payment reference (`payment_reference`, RF\u2026) a payer quotes at a partner bank or PSP (BIL-5.8). Records `bill_invoice.payment_link_issued`. The link's payments run in this key's mode. Requires `invoices:write`.",
        "parameters": [
          {
            "name": "number",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The biller's invoice number."
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice, with its new `pay_url`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "number": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "payment_reference": {
                      "type": "string"
                    },
                    "pay_url": {
                      "type": "string"
                    },
                    "qr": {
                      "type": "object",
                      "description": "Its QR while it takes payments (BIL-5.12), on a read of one invoice: `url` is the invoice's payment link, which any phone camera opens; render it, or use the rendered images.",
                      "properties": {
                        "url": {
                          "type": "string"
                        },
                        "svg_url": {
                          "type": "string"
                        },
                        "png_url": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "number": "FT-2026-0412",
                  "status": "open",
                  "payment_reference": "RF47 1234 5678 9012",
                  "pay_url": "https://pay.kulpay.co.mz/pay/invoice/vY1q8m2Kp9sXc3RtLwB7nZ4hJ6fD0aE5gU8iO2yT1kM"
                }
              }
            }
          },
          "404": {
            "description": "No such 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`: the invoice takes no more payments.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invoice_closed",
                  "detail": "invoice FT-2026-0412 is paid",
                  "error": {
                    "code": "invoice_closed",
                    "message": "invoice FT-2026-0412 is paid"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pay/invoice/{token}": {
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "showInvoicePaymentPage",
        "summary": "An invoice's payment page",
        "security": [],
        "description": "Public, no key: the token in the invoice's `pay_url` is the capability (BIL-5.9). Shows the biller, the invoice, the open amount and the payer's fee, and the RF reference to pay at a partner bank. 404 for an unknown or reissued token.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The HTML page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such link: the unavailable-link page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "payInvoiceFromPage",
        "summary": "Pay an invoice from its page",
        "security": [],
        "description": "Form post (with the page's CSRF token) of the open amount, or `amount` when the biller takes partial payments: KulPay quotes it, collecting on the biller's behalf on its platform account, and redirects (303) to the hosted checkout for the total, where the payer chooses M-Pesa, e-Mola, a card and so on. Once the checkout payment succeeds the invoice is paid (funding `collect`) and the biller is told with `bill_invoice.payment_received`.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "string"
                  },
                  "_csrf": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "303": {
            "description": "To /checkout/{id}."
          },
          "404": {
            "description": "No such link: the unavailable-link page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "422": {
            "description": "The amount is not accepted, or the invoice takes no more payments: the page again, with the reason.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/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`), or, with `enrolments: offer`, offer the biller account to them (`bill_subscription.offered`, status `offered`): nothing of it is delivered until the payer accepts by subscribing with their consent, or declines by opting out. `enrolments` is kept when left out. 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"
                  },
                  "enrolments": {
                    "type": "string",
                    "enum": [
                      "subscribe",
                      "offer"
                    ],
                    "description": "What a biller's matching enrolment does: subscribe (default; the payer can opt out) or offer (the payer is asked first)."
                  }
                }
              },
              "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.macuacuaexample.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.cossaexample.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. An account of a biller that pushes or files its invoices is `active` only when the biller's enrolment of that account names the payer's phone or email; otherwise it is `pending_verification`, with nothing delivered or listed, until such an enrolment arrives. Opting in again after opting out reactivates the subscription, and subscribing to an `offered` one accepts it. 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}/autopay": {
      "put": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "setAutopay",
        "summary": "Set up a payer's autopay",
        "description": "Gives (or changes) the payer's mandate to pay the subscription's invoices automatically (BIL-5.10): `funding` `balance` (your balance; debit the payer's own account when told `bill_autopay.succeeded`) or `collect` (their wallet: `collect.method` mpesa, emola or mkesh, `collect.phone`, the payer's phone when left out); `max_amount`, the most one invoice is debited for; `days_before_due` (0 to 15); and the payer's consent to the autopay terms. Records `bill_autopay.mandate_set`. Each invoice delivered to the subscription, and each one open now, is scheduled for the later of its due date less `days_before_due` and now plus the pre-debit notice (`bills.autopay_notice_hours`, at least 24 hours in live mode), and you are told at once with `bill_autopay.scheduled`: notify the payer before the debit. An invoice above `max_amount` is not debited (`bill_autopay.skipped`, `over_limit`). A failed debit is tried again (`bill_autopay.attempt_failed`) up to `bills.autopay_attempts` times, then given up (`bill_autopay.failed`). The mandate runs in your key's mode. 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"
            },
            "description": "The subscription id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "max_amount",
                  "consent"
                ],
                "properties": {
                  "funding": {
                    "type": "string",
                    "enum": [
                      "balance",
                      "collect"
                    ],
                    "default": "balance"
                  },
                  "collect": {
                    "type": "object",
                    "properties": {
                      "method": {
                        "type": "string",
                        "enum": [
                          "mpesa",
                          "emola",
                          "mkesh"
                        ]
                      },
                      "phone": {
                        "type": "string"
                      },
                      "network": {
                        "type": "string"
                      }
                    }
                  },
                  "max_amount": {
                    "type": "string"
                  },
                  "days_before_due": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 15,
                    "default": 0
                  },
                  "consent": {
                    "type": "object",
                    "required": [
                      "accepted",
                      "text_version"
                    ],
                    "properties": {
                      "accepted": {
                        "type": "boolean"
                      },
                      "text_version": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "funding": "collect",
                "collect": {
                  "method": "mpesa",
                  "phone": "258841110000"
                },
                "max_amount": "2000.00",
                "days_before_due": 3,
                "consent": {
                  "accepted": true,
                  "text_version": "autopay-terms-v1"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The mandate, `active`, with its scheduled invoices.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subscription_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "cancelled"
                      ]
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance",
                        "collect"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "phone_masked": {
                      "type": "string"
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "days_before_due": {
                      "type": "integer"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancelled_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancel_reason": {
                      "type": "string"
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          },
                          "invoice_number": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "scheduled",
                              "processing",
                              "paid",
                              "skipped",
                              "failed"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "scheduled_for": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "description": "Why it was skipped or failed: over_limit, payer_skipped, mandate_cancelled, invoice_<status>, or the failure code and message."
                          }
                        }
                      },
                      "description": "The mandate's recent invoices, newest first."
                    }
                  }
                },
                "example": {
                  "id": "3KtqA2xYz9wV8uT7sR6qP5oN4mL",
                  "subscription_id": "3Kt7ugR1VHfcfHLgVg7vXkohaqR",
                  "status": "active",
                  "mode": "live",
                  "funding": "collect",
                  "method": "mpesa",
                  "phone_masked": "25884****000",
                  "max_amount": "2000.00",
                  "currency": "MZN",
                  "days_before_due": 3,
                  "consent_version": "autopay-terms-v1",
                  "consent_at": "2026-10-19T09:30:00Z",
                  "runs": [
                    {
                      "id": "3KtqA1vRk0hS2bWfZ8yYc4mNpQe",
                      "invoice_id": "3KtqA0aB2cD3eF4gH5iJ6kL7mN8",
                      "invoice_number": "FT-2026-0412",
                      "status": "scheduled",
                      "amount": "1500.00",
                      "currency": "MZN",
                      "scheduled_for": "2026-11-07T00:00:00Z",
                      "next_attempt_at": "2026-11-07T00:00:00Z",
                      "attempts": 0
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid funding, wallet, max_amount or days_before_due, or the subscription is not active.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "max_amount, the most one invoice may be debited for, is a positive decimal",
                  "error": {
                    "code": "invalid_request",
                    "message": "max_amount, the most one invoice may be debited for, is a positive decimal"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such payer or subscription.",
            "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`: the payer's consent to the autopay terms.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Entity",
                  "status": 422,
                  "code": "consent_required",
                  "detail": "the payer's consent to autopay is required: consent.accepted and the version of the terms they accepted",
                  "error": {
                    "code": "consent_required",
                    "message": "the payer's consent to autopay is required: consent.accepted and the version of the terms they accepted"
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "getAutopay",
        "summary": "A payer's autopay",
        "description": "The subscription's mandate and its recent invoices (BIL-5.10). Requires `bills:read`.",
        "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"
            },
            "description": "The subscription id."
          }
        ],
        "responses": {
          "200": {
            "description": "The mandate.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subscription_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "cancelled"
                      ]
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance",
                        "collect"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "phone_masked": {
                      "type": "string"
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "days_before_due": {
                      "type": "integer"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancelled_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancel_reason": {
                      "type": "string"
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          },
                          "invoice_number": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "scheduled",
                              "processing",
                              "paid",
                              "skipped",
                              "failed"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "scheduled_for": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "description": "Why it was skipped or failed: over_limit, payer_skipped, mandate_cancelled, invoice_<status>, or the failure code and message."
                          }
                        }
                      },
                      "description": "The mandate's recent invoices, newest first."
                    }
                  }
                },
                "example": {
                  "id": "3KtqA2xYz9wV8uT7sR6qP5oN4mL",
                  "subscription_id": "3Kt7ugR1VHfcfHLgVg7vXkohaqR",
                  "status": "active",
                  "mode": "live",
                  "funding": "collect",
                  "method": "mpesa",
                  "phone_masked": "25884****000",
                  "max_amount": "2000.00",
                  "currency": "MZN",
                  "days_before_due": 3,
                  "consent_version": "autopay-terms-v1",
                  "consent_at": "2026-10-19T09:30:00Z",
                  "runs": [
                    {
                      "id": "3KtqA1vRk0hS2bWfZ8yYc4mNpQe",
                      "invoice_id": "3KtqA0aB2cD3eF4gH5iJ6kL7mN8",
                      "invoice_number": "FT-2026-0412",
                      "status": "scheduled",
                      "amount": "1500.00",
                      "currency": "MZN",
                      "scheduled_for": "2026-11-07T00:00:00Z",
                      "next_attempt_at": "2026-11-07T00:00:00Z",
                      "attempts": 0
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such payer, subscription or mandate.",
            "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"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "cancelAutopay",
        "summary": "Cancel a payer's autopay",
        "description": "Cancels the mandate at once (BIL-5.10): its scheduled invoices are not debited (`bill_autopay.skipped`, `mandate_cancelled`). Optional `?reason=`. Records `bill_autopay.mandate_cancelled`; opting the payer out of the subscription cancels it too. 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"
            },
            "description": "The subscription id."
          },
          {
            "name": "reason",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The mandate, `cancelled`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subscription_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "cancelled"
                      ]
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance",
                        "collect"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "phone_masked": {
                      "type": "string"
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "days_before_due": {
                      "type": "integer"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancelled_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancel_reason": {
                      "type": "string"
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          },
                          "invoice_number": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "scheduled",
                              "processing",
                              "paid",
                              "skipped",
                              "failed"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "scheduled_for": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "description": "Why it was skipped or failed: over_limit, payer_skipped, mandate_cancelled, invoice_<status>, or the failure code and message."
                          }
                        }
                      },
                      "description": "The mandate's recent invoices, newest first."
                    }
                  }
                },
                "example": {
                  "id": "3KtqA2xYz9wV8uT7sR6qP5oN4mL",
                  "subscription_id": "3Kt7ugR1VHfcfHLgVg7vXkohaqR",
                  "status": "cancelled",
                  "mode": "live",
                  "funding": "collect",
                  "method": "mpesa",
                  "phone_masked": "25884****000",
                  "max_amount": "2000.00",
                  "currency": "MZN",
                  "days_before_due": 3,
                  "consent_version": "autopay-terms-v1",
                  "consent_at": "2026-10-19T09:30:00Z",
                  "runs": [
                    {
                      "id": "3KtqA1vRk0hS2bWfZ8yYc4mNpQe",
                      "invoice_id": "3KtqA0aB2cD3eF4gH5iJ6kL7mN8",
                      "invoice_number": "FT-2026-0412",
                      "status": "skipped",
                      "amount": "1500.00",
                      "currency": "MZN",
                      "scheduled_for": "2026-11-07T00:00:00Z",
                      "attempts": 0,
                      "reason": "mandate_cancelled"
                    }
                  ],
                  "cancelled_at": "2026-10-20T08:00:00Z",
                  "cancel_reason": "payer asked"
                }
              }
            }
          },
          "404": {
            "description": "No such payer, subscription or mandate.",
            "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/{sid}/autopay/skip": {
      "post": {
        "tags": [
          "Bill payments"
        ],
        "operationId": "skipAutopay",
        "summary": "Skip one invoice's autopay",
        "description": "The invoice is not debited (BIL-5.10): `bill_autopay.skipped`, `payer_skipped`. Only a scheduled invoice can be skipped. 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"
            },
            "description": "The subscription id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "invoice_id"
                ],
                "properties": {
                  "invoice_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "invoice_id": "3KtqA0aB2cD3eF4gH5iJ6kL7mN8"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The mandate.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "subscription_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "cancelled"
                      ]
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "funding": {
                      "type": "string",
                      "enum": [
                        "balance",
                        "collect"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "phone_masked": {
                      "type": "string"
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "days_before_due": {
                      "type": "integer"
                    },
                    "consent_version": {
                      "type": "string"
                    },
                    "consent_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancelled_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "cancel_reason": {
                      "type": "string"
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "invoice_id": {
                            "type": "string"
                          },
                          "invoice_number": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "scheduled",
                              "processing",
                              "paid",
                              "skipped",
                              "failed"
                            ]
                          },
                          "amount": {
                            "type": "string"
                          },
                          "currency": {
                            "type": "string"
                          },
                          "scheduled_for": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "bill_payment_id": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string",
                            "description": "Why it was skipped or failed: over_limit, payer_skipped, mandate_cancelled, invoice_<status>, or the failure code and message."
                          }
                        }
                      },
                      "description": "The mandate's recent invoices, newest first."
                    }
                  }
                },
                "example": {
                  "id": "3KtqA2xYz9wV8uT7sR6qP5oN4mL",
                  "subscription_id": "3Kt7ugR1VHfcfHLgVg7vXkohaqR",
                  "status": "active",
                  "mode": "live",
                  "funding": "collect",
                  "method": "mpesa",
                  "phone_masked": "25884****000",
                  "max_amount": "2000.00",
                  "currency": "MZN",
                  "days_before_due": 3,
                  "consent_version": "autopay-terms-v1",
                  "consent_at": "2026-10-19T09:30:00Z",
                  "runs": [
                    {
                      "id": "3KtqA1vRk0hS2bWfZ8yYc4mNpQe",
                      "invoice_id": "3KtqA0aB2cD3eF4gH5iJ6kL7mN8",
                      "invoice_number": "FT-2026-0412",
                      "status": "skipped",
                      "amount": "1500.00",
                      "currency": "MZN",
                      "scheduled_for": "2026-11-07T00:00:00Z",
                      "attempts": 0,
                      "reason": "payer_skipped"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The invoice is no longer scheduled.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the invoice's autopay is paid and can no longer be skipped",
                  "error": {
                    "code": "invalid_request",
                    "message": "the invoice's autopay is paid and can no longer be skipped"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such payer, subscription, mandate or scheduled 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 payer, subscription or enrolment",
                  "error": {
                    "code": "not_found",
                    "message": "no such payer, subscription or enrolment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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), or declines an `offered` one: nothing of that biller account is delivered any more, and a later enrolment by the biller does not subscribe (nor offer) it to 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`.",
        "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"
                        }
                      }
                    }
                  }
                }
              },
              "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. 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"
                          }
                        }
                      }
                    },
                    "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"
                          },
                          "bank_reference": {
                            "type": "string"
                          },
                          "approval_id": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "paid_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "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"
                          }
                        }
                      }
                    },
                    "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 as paid",
        "description": "The bank transfer that paid the biller: its reference and a reason. A second person approves; the biller payable is then cleared against client funds and the biller receives `bill_remittance.paid`. 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"
                          }
                        }
                      }
                    },
                    "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`.",
        "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. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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`. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      },
      "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"
                    }
                  },
                  "application_id": {
                    "type": "string",
                    "description": "A merchant application of the account in the key's mode (phase 6); the key's requests are that application's."
                  }
                }
              },
              "example": {
                "name": "Electricidade de Mo\u00e7ambique",
                "type": "sk",
                "scopes": [
                  "payments:write"
                ],
                "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
              }
            }
          }
        },
        "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."
                    },
                    "application_id": {
                      "type": "string"
                    }
                  }
                },
                "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",
                  "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        },
        "description": "An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret shown once and is never kept: the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`."
      }
    },
    "/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. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret shown once and is never kept: the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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`; `idempotency_replay_unavailable`, `idempotency_key_reused`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "already_rolled": {
                    "value": {
                      "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"
                      }
                    }
                  },
                  "idempotency_replay_unavailable": {
                    "value": {
                      "type": "about:blank",
                      "title": "Conflict",
                      "status": 409,
                      "code": "idempotency_replay_unavailable",
                      "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                      "error": {
                        "code": "idempotency_replay_unavailable",
                        "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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`. The `url` must be an absolute `https` URL (plain `http` only to loopback, or on a stack whose dial policy allows private destinations), without credentials (`user:pass@`), at most 500 characters, and never a link-local (169.254.x.x, the cloud metadata address), multicast, unspecified or reserved address; otherwise `400 invalid_request`, the reason in `detail` (QA-FN-02). Requires `webhooks:manage`, or `service:webhooks:write` with `account_id`. With `application_id` the endpoint is scoped to a merchant application and receives only the events of that application's payments (KulOffice phase 6); account-wide endpoints receive everything, as before. A trusted service (KulOffice) acts for a merchant by passing `account_id`, with the operator-issued `service:webhooks:write` (owner decision 2026-10-12); without that scope another account is `403 insufficient_scope` whatever the key's merchant scopes, and an unknown account `404 account_not_found`. Without `account_id` (or with the caller's own) the call acts on the key's own account and needs the merchant scope, as before. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`. Mode (IG-76): omitted, the key's own. A trusted service acting for another account may name `mode` test or live, never above its key's mode (a test-mode key: test only, `403 mode_not_allowed`); so a live-mode `service:webhooks:write` key also manages a merchant's test-mode endpoints. On the caller's own account `mode` may only repeat the key's.",
        "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"
                    }
                  },
                  "application_id": {
                    "type": "string",
                    "description": "Scope the endpoint to an active merchant application of the account in the key's mode (KulOffice phase 6): it then receives only the events of payments made with that application's keys. A key that belongs to an application scopes to it by default and may name no other. Omit for an account-wide endpoint."
                  },
                  "account_id": {
                    "type": "string",
                    "description": "The merchant account a trusted service (KulOffice) creates the endpoint for; needs `service:webhooks:write`. `application_id` must then be an active application of that account in the key's mode. Omit for the key's own account."
                  },
                  "mode": {
                    "enum": [
                      "live",
                      "test"
                    ],
                    "description": "The endpoint's mode (IG-76). Omitted: the key's own. Another mode only for a trusted service acting for another account, and never live from a test-mode key."
                  }
                }
              },
              "examples": {
                "account_wide": {
                  "summary": "Account-wide",
                  "value": {
                    "url": "https://merchant.example.co.mz/kulpay/return",
                    "event_types": [
                      "payment.succeeded"
                    ],
                    "contacts": [
                      "opsexample.co.mz"
                    ]
                  }
                },
                "application": {
                  "summary": "Scoped to a merchant application",
                  "value": {
                    "url": "https://shop.example.co.mz/kulpay/webhooks",
                    "event_types": [
                      "payment.*"
                    ],
                    "application_id": "3KfOz1mApplicationWeb00001"
                  }
                },
                "service": {
                  "summary": "KulOffice, for a merchant's application",
                  "value": {
                    "account_id": "3KfOz1mMerchantAccount0001",
                    "url": "https://kuloffice.example.co.mz/gateway/webhooks",
                    "event_types": [
                      "payment.*"
                    ],
                    "application_id": "3KfOz1mApplicationWeb00001"
                  }
                }
              }
            }
          }
        },
        "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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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 URL KulPay does not deliver to (not https, credentials in it, too long, a link-local, multicast or reserved 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)"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Another `account_id` without `service:webhooks:write` (a merchant key, whatever its scopes, or no key); or the caller's own account without `webhooks:manage`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:webhooks:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:webhooks:write"
                  },
                  "missing_scope": "service:webhooks:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: `account_id` names no account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account with id 3KmissingAccount000000000001 exists on this deployment.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account with id 3KmissingAccount000000000001 exists on this deployment."
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      },
      "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"
                          },
                          "application_id": {
                            "type": "string",
                            "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                          }
                        }
                      }
                    },
                    "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": [
                        "devexample.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
                }
              }
            }
          },
          "403": {
            "description": "Another `account_id` without `service:webhooks:write` (a merchant key, whatever its scopes, or no key); or the caller's own account without `webhooks:manage`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:webhooks:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:webhooks:write"
                  },
                  "missing_scope": "service:webhooks:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: `account_id` names no account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account with id 3KmissingAccount000000000001 exists on this deployment.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account with id 3KmissingAccount000000000001 exists on this deployment."
                  }
                }
              }
            }
          }
        },
        "description": "The key's account's endpoints, without their secrets. Requires `webhooks:manage` or `admin:read`, or `service:webhooks:write` with `account_id`. A trusted service (KulOffice) acts for a merchant by passing `account_id`, with the operator-issued `service:webhooks:write` (owner decision 2026-10-12); without that scope another account is `403 insufficient_scope` whatever the key's merchant scopes, and an unknown account `404 account_not_found`. Without `account_id` (or with the caller's own) the call acts on the key's own account and needs the merchant scope, as before. A trusted service may filter by `mode` (IG-76); a test-mode service key lists test-mode endpoints only.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The merchant account a trusted service acts for (needs `service:webhooks:write`). Omit for the key's own account."
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "With `account_id` (a trusted service): one mode; omitted: both (a test-mode key: test)."
          }
        ]
      }
    },
    "/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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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": [
                    "opsexample.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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "The merchant account a trusted service acts for (needs `service:webhooks:write`). Omit for the key's own account."
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "403": {
            "description": "Another `account_id` without `service:webhooks:write` (a merchant key, whatever its scopes, or no key); or the caller's own account without `webhooks:manage`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:webhooks:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:webhooks:write"
                  },
                  "missing_scope": "service:webhooks:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: `account_id` names no account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account with id 3KmissingAccount000000000001 exists on this deployment.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account with id 3KmissingAccount000000000001 exists on this deployment."
                  }
                }
              }
            }
          }
        },
        "description": "Removes one of the account's endpoints. Requires `webhooks:manage`, or `service:webhooks:write` with `account_id` (the endpoint must belong to that account). A trusted service (KulOffice) acts for a merchant by passing `account_id`, with the operator-issued `service:webhooks:write` (owner decision 2026-10-12); without that scope another account is `403 insufficient_scope` whatever the key's merchant scopes, and an unknown account `404 account_not_found`. Without `account_id` (or with the caller's own) the call acts on the key's own account and needs the merchant scope, as before. A test-mode service key does not reach a live endpoint (404, IG-76)."
      }
    },
    "/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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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"
                        },
                        "application_id": {
                          "type": "string",
                          "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                        }
                      }
                    },
                    "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": [
                      "devexample.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"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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": [
                    "devexample.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"
                    },
                    "application_id": {
                      "type": "string",
                      "description": "The merchant application the endpoint is scoped to: it receives the events of payments made with that application's keys only (signed with its own secret, same schedule, dead-letter queue and replay). Absent: account-wide, every event of the account."
                    }
                  }
                },
                "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": [
                    "opsexample.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": [
                  "opsexample.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. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret shown once and is never kept: the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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.",
                  "legacy_code": "invalid_body",
                  "error": {
                    "code": "invalid_body",
                    "message": "The request body could not be parsed as valid JSON."
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/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/qr/resolve": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "resolveQR",
        "summary": "Resolve a scanned bill QR",
        "description": "Your app read a QR: send the link it encodes. A KulPay bill QR (BIL-5.12, BIL-5.13) is a plain https link: an invoice's payment link (`/pay/invoice/{token}`) or a customer account's link (`/pay/account/{token}`). It answers exactly as `POST /v1/name-lookups` does for a bill (BIL-5.11): the verified biller, the amount due now and fee-inclusive `constraints`, a `lookup_id` to initiate and confirm the deposit, plus `qr`, what the QR named (`kind` invoice or account). Show the payer the `destination` before taking the money. An account's link resolves to the account's open invoices, oldest due first (`ambiguous` when there are several: ask the payer and `select`), or to the meter at the supplier. A link that is not KulPay's (a reissued or made-up token) is 400; anything else is 422 `not_supported`. Shares the lookups' limit of 60 per minute per key. Requires `transactions:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payload"
                ],
                "properties": {
                  "payload": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "payload": "https://pay.kulpay.co.mz/pay/invoice/q7Zt0nY4w1cVd8Lm2sPe5A"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The bill lookup.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "lookup_id": "3KuE8i149W57HQNBBq6yz78I32z",
                  "status": "found",
                  "method": "cash_deposit",
                  "reference": "RF471234567890120",
                  "cached": false,
                  "destination": {
                    "option_id": "AGUAS:INVOICE:F-Q",
                    "kind": "kulpay_invoice",
                    "biller": "AGUAS",
                    "biller_name": "\u00c1guas da Regi\u00e3o",
                    "invoice_number": "F-Q",
                    "account_masked": "****-Q",
                    "amount_due": "640.00",
                    "payer_fee": "0.00",
                    "constraints": {
                      "currency": "MZN",
                      "exact_amount": "640.00"
                    }
                  },
                  "constraints": {
                    "currency": "MZN",
                    "exact_amount": "640.00"
                  },
                  "expires_at": "2026-10-19T09:30:00Z",
                  "qr": {
                    "kind": "invoice",
                    "biller": "AGUAS",
                    "invoice_number": "F-Q"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A KulPay link whose token is not valid: reissued, mistyped or made up.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "this invoice link is not valid (it was reissued, or mistyped)",
                  "error": {
                    "code": "invalid_request",
                    "message": "this invoice link is not valid (it was reissued, or mistyped)"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Not a KulPay bill link.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Entity",
                  "status": 422,
                  "code": "not_supported",
                  "detail": "this QR is not a KulPay bill link (/pay/invoice/… or /pay/account/…)",
                  "error": {
                    "code": "not_supported",
                    "message": "this QR is not a KulPay bill link (/pay/invoice/… or /pay/account/…)"
                  }
                }
              }
            }
          },
          "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": "Lookups, QR resolutions included, are limited to 60 per minute per key.",
                  "error": {
                    "code": "rate_limited",
                    "message": "Lookups, QR resolutions included, are limited to 60 per minute per key."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/name-lookups/{id}/select": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "selectNameLookupOption",
        "summary": "Choose a bill reference's option",
        "description": "An ambiguous bill reference (BIL-5.11) names several things (an invoice and an account, two products): the payer chooses one `option_id`, and the lookup becomes `found` with it (`name_lookup.option_selected`). The same choice again returns it; another once a deposit started from the lookup is 409 `option_locked`. Requires `transactions:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The lookup_id."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "option_id"
                ],
                "properties": {
                  "option_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "option_id": "AGUAS:INVOICE:CL-0042"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The lookup, found with the option.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "lookup_id": "3KuE8i149W57HQNBBq6yz78I32z",
                  "status": "found",
                  "method": "cash_deposit",
                  "reference": "CL-0042",
                  "cached": false,
                  "destination": {
                    "option_id": "AGUAS:INVOICE:CL-0042",
                    "kind": "kulpay_invoice",
                    "biller": "AGUAS",
                    "biller_name": "\u00c1guas da Regi\u00e3o",
                    "invoice_number": "CL-0042",
                    "account_masked": "***0042",
                    "amount_due": "300.00",
                    "payer_fee": "10.00",
                    "constraints": {
                      "currency": "MZN",
                      "exact_amount": "310.00"
                    }
                  },
                  "constraints": {
                    "currency": "MZN",
                    "exact_amount": "310.00"
                  },
                  "expires_at": "2026-10-19T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Unknown lookup or option.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "no such option_id for this lookup",
                  "error": {
                    "code": "invalid_request",
                    "message": "no such option_id for this lookup"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`option_locked`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "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/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 platform's `kulpay_cms` connector, which serves every partner (no binding of its own is needed; the partner's capability matrix needs a NAME_LOOKUP row for method `cash_deposit`), 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), except a `cash_deposit` reference: it is always answered live and gets a new `lookup_id`, because whether a reference can take money changes (a request closes, a limit is reached); 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`. The launch capability matrix (plan phase 10) holds the route first: one it does not enable is `422 route_not_enabled`, and anything off_net `422 movement_scope_disabled`, before any limit is reserved or provider called.\n\n**Bills at the counter (BIL-5.11).** The same call looks up a bill. A valid ISO 11649 RF reference (an invoice's `payment_reference`, typed with spaces or not) is that invoice. With `biller` (and optionally `product`) the cashier chose from `GET /v1/billers`, KulPay looks among that biller's invoices (by number as written, without separators or case, or by KulPay ID) and asks its products' suppliers (a meter, an account). The answer has a `destination` (`kind` `kulpay_invoice` or `kulpay_bill`, the biller, product, invoice number, the holder's name masked, the amount due and the payer's fee) and `constraints` that include the fee: what the payer must bring. Several matches answer `ambiguous` with `options`: the payer chooses (`POST /v1/name-lookups/{id}/select`); KulPay never guesses. None answers `not_found` with the `reason` (for example \"the invoice is paid\"). Then initiate and confirm the deposit as for any reference: your confirmation is trusted, and the bill is paid at once with you as its channel.\n\n**References of billers' own (BIL-5.14).** Many billers keep their own references (a meter, a contract, a student number) and answer for them at their own lookup. A reference without `biller` that is no RF reference is sent to the billers whose routable products claim its shape (their `reference_pattern`), at most 5, each asked in parallel for at most 3 s: one that knows it is `found`; several are `ambiguous`, each option naming its biller (`biller`, `biller_name`, `logo_url`) for the payer to choose; a biller that fails or is late is left out; when none answered with an option and some failed or were late, the answer is `503 biller_unavailable` with `Retry-After`: try again (the reference may be theirs, so it is not taken as a wallet deposit). When more than 5 billers claim the shape, the answer is `biller_required` with `billers` (code, name, logo): ask the payer which, and look up again with `biller`. When no biller knows the reference, the lookup goes on to the deposit resolver (a wallet or account), as before.",
        "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)."
                  },
                  "biller": {
                    "type": "string",
                    "example": "AGUAS",
                    "description": "A bill (BIL-5.11): the biller the cashier chose; KulPay looks the reference up among its invoices and products. Not needed for an RF reference."
                  },
                  "product": {
                    "type": "string",
                    "example": "CREDELEC",
                    "description": "With biller: only this product."
                  }
                }
              },
              "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"
                      ]
                    },
                    "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."
                    },
                    "details": {
                      "type": "object",
                      "description": "A found `cash_deposit` reference: what it is for, when its owner says (a KulOffice money request). Display data for the payer; every field optional; absent when there are none.",
                      "properties": {
                        "kind": {
                          "type": "string",
                          "description": "For example `money_request`."
                        },
                        "amount_due": {
                          "type": "string",
                          "description": "A decimal amount, for example `1500.00`."
                        },
                        "currency": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string"
                        },
                        "due_date": {
                          "type": "string",
                          "description": "`YYYY-MM-DD` or RFC 3339."
                        },
                        "reference": {
                          "type": "string"
                        }
                      }
                    },
                    "destination": {
                      "type": "object",
                      "description": "A bill (BIL-5.11): what the deposit pays.",
                      "properties": {
                        "option_id": {
                          "type": "string"
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "kulpay_invoice",
                            "kulpay_bill"
                          ]
                        },
                        "biller": {
                          "type": "string"
                        },
                        "biller_name": {
                          "type": "string"
                        },
                        "logo_url": {
                          "type": "string"
                        },
                        "product": {
                          "type": "string"
                        },
                        "invoice_number": {
                          "type": "string"
                        },
                        "masked_name": {
                          "type": "string"
                        },
                        "account_masked": {
                          "type": "string"
                        },
                        "amount_due": {
                          "type": "string"
                        },
                        "payer_fee": {
                          "type": "string",
                          "description": "The payer's fee on the amount due."
                        },
                        "constraints": {
                          "type": "object",
                          "description": "The amounts it takes, the payer's fee included."
                        }
                      }
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "A bill (BIL-5.11): what the deposit pays.",
                        "properties": {
                          "option_id": {
                            "type": "string"
                          },
                          "kind": {
                            "type": "string",
                            "enum": [
                              "kulpay_invoice",
                              "kulpay_bill"
                            ]
                          },
                          "biller": {
                            "type": "string"
                          },
                          "biller_name": {
                            "type": "string"
                          },
                          "logo_url": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "invoice_number": {
                            "type": "string"
                          },
                          "masked_name": {
                            "type": "string"
                          },
                          "account_masked": {
                            "type": "string"
                          },
                          "amount_due": {
                            "type": "string"
                          },
                          "payer_fee": {
                            "type": "string",
                            "description": "The payer's fee on the amount due."
                          },
                          "constraints": {
                            "type": "object",
                            "description": "The amounts it takes, the payer's fee included."
                          }
                        }
                      },
                      "description": "An ambiguous bill reference's options."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Why a bill reference found nothing to pay."
                    }
                  }
                },
                "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",
                  "legacy_code": "gateway.invalid_request",
                  "error": {
                    "code": "gateway.invalid_request",
                    "message": "a method and an MSISDN (or account) are required"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`not_supported`: the method cannot look names up. Or the route is not in the launch matrix (route_not_enabled, movement_scope_disabled).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "default": {
                    "summary": "As documented",
                    "value": {
                      "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"
                      }
                    }
                  },
                  "route_not_enabled": {
                    "summary": "Route not in the launch matrix",
                    "value": {
                      "type": "about:blank",
                      "title": "Route not enabled",
                      "status": 422,
                      "code": "route_not_enabled",
                      "detail": "the charge route settling/mpesa (live, acc_top) is not enabled in launch matrix 2026-10-12.1",
                      "rule": "launch_matrix",
                      "rejection_id": "3KfS0route0not0enabled00001"
                    }
                  }
                }
              }
            }
          },
          "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.",
                  "legacy_code": "gateway.rate_limited",
                  "error": {
                    "code": "gateway.rate_limited",
                    "message": "Name lookups are limited to 60 per minute per key."
                  }
                }
              }
            }
          },
          "502": {
            "description": "The provider did not 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.",
                  "legacy_code": "gateway.provider_error",
                  "error": {
                    "code": "gateway.provider_error",
                    "message": "The provider could not answer the lookup. Try again later."
                  }
                }
              }
            }
          },
          "503": {
            "description": "`biller_unavailable` (BIL-5.14): a reference without `biller` that billers' routable products claim, and none of them answered with an option while some failed or were late. The reference may be theirs, so it is not taken as a wallet deposit: try again after `Retry-After`.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds."
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Biller unavailable",
                  "status": 503,
                  "code": "biller_unavailable",
                  "detail": "The billers this reference may belong to did not answer (EDM, LUZ). Try again in a moment."
                }
              }
            }
          }
        }
      }
    },
    "/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`).",
        "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"
                              ]
                            }
                          }
                        },
                        "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"
                              ]
                            }
                          }
                        },
                        "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"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/payments": {
      "get": {
        "tags": [
          "Payments"
        ],
        "summary": "Find a payment by your payment_id",
        "description": "Returns the snapshot of the payment your own `payment_id` names (the `payment_id` in the charge body, or the `Idempotency-Key` header it was charged with when the two differ; QA-FN-03), and the snapshot echoes the body's `payment_id`, within the key's account and mode. A caller that lost the charge response recovers the payment by reading it, never by charging again. A `payment_id` stays bound to its amount, currency, method, payer and metadata for as long as the payment exists: charging it again with the same request returns this payment, with another request it is `409 idempotency_key_reused`. `404` means only that no such payment is visible to this key.",
        "operationId": "findPaymentSnapshot",
        "parameters": [
          {
            "name": "payment_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your reference: the `payment_id` (Idempotency-Key) sent on `POST /charges`."
          }
        ],
        "responses": {
          "200": {
            "description": "Snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentSnapshot"
                },
                "example": {
                  "payment_id": "ins_3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "provider_payment_id": "3KUXQ5NPx0w8qZ1Ff4cWm2bDkTe",
                  "status": "succeeded",
                  "money": {
                    "amount": "250.50",
                    "currency": "MZN"
                  },
                  "source": {
                    "type": "mobile_wallet",
                    "mobile_wallet": {
                      "network": "mpesa",
                      "phone_number": "+258840000001"
                    }
                  },
                  "last_updated_at": "2026-10-10T09:30:00Z",
                  "metadata": {
                    "kulpay_reference": "ins_3KUXQ43bztiuf9tCMNgbcrYvne3"
                  },
                  "mode": "live",
                  "amount_minor": 25050,
                  "connector_binding_id": "3KUXPz2c8HfX1n0Qe7LwYb5RtAs",
                  "connector_ref": "MP261010.0930.A12345",
                  "settled_at": "2026-10-10T09:30:00Z",
                  "settled_amount": "250.50",
                  "settled_amount_minor": 25050
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/incoming-transfers": {
      "post": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "reportIncomingTransfer",
        "summary": "Report an incoming transfer",
        "description": "A bank or PSP certified for the `account_funding.incoming` capability (an approved capability matrix entry for the mode and currency; live needs a partner in pilot or live) reports a transfer it received for a KulPay beneficiary, named by a `deposit_reference`, an `alias` (deposit tag) or a `virtual_account`. Amounts are integer minor units. Any other partner is refused (`403 capability_not_enabled`, or `not_a_partner` for a key that is no partner's) before anything is stored.\n\nThe transfer is idempotent by your `partner_reference` in the mode: the same report returns the same transfer (`200`), the same reference with other facts is `409 partner_reference_conflict`. The gateway resolves the beneficiary privately: you see whether it `matched` and the holder's masked name, never its account. Finality is explicit: send `final: true` only for money that cannot be recalled, else confirm it later with `POST /v1/incoming-transfers/{id}/finality`. Only a matched, final transfer is `creditable`. Unmatched final money stays in suspense until an operator binds it or returns it (you then return the funds). A transfer an operator holds for review is shown to the reporting partner in the ordinary state its facts give (`matched`, `confirmed_final` or `unmatched`), in reads, lists, the status filter, events and webhooks; `compliance_review` and its event go only to the beneficiary and operators.\n\nEvents and webhooks: `incoming_transfer.received`, `incoming_transfer.matched`, `incoming_transfer.unmatched`, `incoming_transfer.finality_confirmed`, `incoming_transfer.confirmed_final`, `incoming_transfer.compliance_review` (beneficiary only), `incoming_transfer.returned`, `incoming_transfer.credited`, with identifiers, amount, status, `final` and `version` only; read the transfer back through this API. Requires `transactions:write`. The launch capability matrix (plan phase 10) holds the route first: one it does not enable is `422 route_not_enabled`, and anything off_net `422 movement_scope_disabled`, before any limit is reserved or provider called.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "partner_reference",
                  "amount_minor",
                  "currency",
                  "destination"
                ],
                "properties": {
                  "partner_reference": {
                    "type": "string",
                    "maxLength": 128,
                    "description": "Your own transfer reference."
                  },
                  "amount_minor": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "currency": {
                    "type": "string",
                    "example": "MZN"
                  },
                  "destination": {
                    "type": "object",
                    "required": [
                      "type",
                      "value"
                    ],
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "deposit_reference",
                          "alias",
                          "virtual_account",
                          "payment_reference"
                        ]
                      },
                      "value": {
                        "type": "string",
                        "minLength": 3,
                        "maxLength": 128
                      }
                    }
                  },
                  "value_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "final": {
                    "type": "boolean",
                    "default": false
                  }
                }
              },
              "example": {
                "partner_reference": "BCI-TRF-20261011-0042",
                "amount_minor": 125000,
                "currency": "MZN",
                "destination": {
                  "type": "deposit_reference",
                  "value": "INV-1001"
                },
                "value_date": "2026-10-11",
                "final": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded now; `status` is `matched`, `unmatched` or, when the beneficiary could not be asked yet, `received`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "example": {
                  "id": "3KdW1pZ0incomingTransfer001",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "partner_reference": "BCI-TRF-20261011-0042",
                  "mode": "live",
                  "status": "matched",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "destination": {
                    "type": "deposit_reference",
                    "value": "INV-1001"
                  },
                  "value_date": "2026-10-11",
                  "final": false,
                  "creditable": false,
                  "beneficiary_name": "L*** D*** L**",
                  "version": 2,
                  "received_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:01Z"
                }
              }
            }
          },
          "200": {
            "description": "The same report was recorded before: that transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "example": {
                  "id": "3KdW1pZ0incomingTransfer001",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "partner_reference": "BCI-TRF-20261011-0042",
                  "mode": "live",
                  "status": "matched",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "destination": {
                    "type": "deposit_reference",
                    "value": "INV-1001"
                  },
                  "value_date": "2026-10-11",
                  "final": false,
                  "creditable": false,
                  "beneficiary_name": "L*** D*** L**",
                  "version": 2,
                  "received_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:01Z"
                }
              }
            }
          },
          "400": {
            "description": "The report is malformed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "destination.type must be deposit_reference, alias or virtual_account",
                  "error": {
                    "code": "invalid_request",
                    "message": "destination.type must be deposit_reference, alias or virtual_account"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not certified for account_funding.incoming (or not a partner: `not_a_partner`). Nothing was stored.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "capability_not_enabled",
                  "detail": "account_funding.incoming is not enabled for this partner: not approved in live mode",
                  "error": {
                    "code": "capability_not_enabled",
                    "message": "account_funding.incoming is not enabled for this partner: not approved in live mode"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The reference was reported with other facts.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "partner_reference_conflict",
                  "detail": "payments: this partner_reference was reported with different facts",
                  "error": {
                    "code": "partner_reference_conflict",
                    "message": "payments: this partner_reference was reported with different facts"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The route is not in the launch matrix (route_not_enabled) or would be off_net (movement_scope_disabled).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Route not enabled",
                  "status": 422,
                  "code": "route_not_enabled",
                  "detail": "the charge route settling/mpesa (live, acc_top) is not enabled in launch matrix 2026-10-12.1",
                  "rule": "launch_matrix",
                  "rejection_id": "3KfS0route0not0enabled00001"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "findIncomingTransfers",
        "summary": "Find incoming transfers by partner reference",
        "description": "The transfers the key's account reported or receives, in the key's mode, by `partner_reference` and/or `status`, newest first. `status` matches the status the key's account is shown: the reporting partner never finds a transfer under `compliance_review`, but under the ordinary state it is shown. Requires `transactions:read`.",
        "parameters": [
          {
            "name": "partner_reference",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "The status the key's account is shown (see IncomingTransfer.status)."
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IncomingTransfer"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KdW1pZ0incomingTransfer001",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "partner_reference": "BCI-TRF-20261011-0042",
                      "mode": "live",
                      "status": "matched",
                      "amount_minor": 125000,
                      "currency": "MZN",
                      "destination": {
                        "type": "deposit_reference",
                        "value": "INV-1001"
                      },
                      "value_date": "2026-10-11",
                      "final": false,
                      "creditable": false,
                      "beneficiary_name": "L*** D*** L**",
                      "version": 2,
                      "received_at": "2026-10-11T09:30:00Z",
                      "updated_at": "2026-10-11T09:30:01Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/incoming-transfers/{id}": {
      "get": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "getIncomingTransfer",
        "summary": "An incoming transfer",
        "description": "For the partner that reported it and, once matched, its beneficiary, in the key's mode. Only the beneficiary sees `beneficiary_account_id`, its credit and `compliance_review`; the partner sees a held transfer in its ordinary state. Credit only when `creditable` is true. Requires `transactions:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transfer, as the caller may see it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "example": {
                  "id": "3KdW1pZ0incomingTransfer001",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "partner_reference": "BCI-TRF-20261011-0042",
                  "mode": "live",
                  "status": "confirmed_final",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "destination": {
                    "type": "deposit_reference",
                    "value": "INV-1001"
                  },
                  "value_date": "2026-10-11",
                  "final": true,
                  "creditable": true,
                  "beneficiary_name": "L*** D*** L**",
                  "version": 3,
                  "received_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:45:00Z",
                  "final_at": "2026-10-11T09:45:00Z",
                  "beneficiary_account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not the caller's (partner or beneficiary) in this mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such incoming transfer",
                  "error": {
                    "code": "not_found",
                    "message": "no such incoming transfer"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/incoming-transfers/{id}/finality": {
      "post": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "confirmIncomingFinality",
        "summary": "Confirm a transfer final",
        "description": "The reporting partner confirms the money is final. A matched transfer becomes `confirmed_final` (`incoming_transfer.confirmed_final`); one without a beneficiary goes to suspense (`incoming_transfer.finality_confirmed`). Confirming again changes nothing. Requires `transactions:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "example": {
                  "id": "3KdW1pZ0incomingTransfer001",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "partner_reference": "BCI-TRF-20261011-0042",
                  "mode": "live",
                  "status": "confirmed_final",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "destination": {
                    "type": "deposit_reference",
                    "value": "INV-1001"
                  },
                  "value_date": "2026-10-11",
                  "final": true,
                  "creditable": true,
                  "beneficiary_name": "L*** D*** L**",
                  "version": 3,
                  "received_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:45:00Z",
                  "final_at": "2026-10-11T09:45:00Z"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not the caller's (partner or beneficiary) in this mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such incoming transfer",
                  "error": {
                    "code": "not_found",
                    "message": "no such incoming transfer"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The transfer was returned.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "the transfer was returned",
                  "error": {
                    "code": "invalid_state",
                    "message": "the transfer was returned"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/incoming-transfers/{id}/credit": {
      "post": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "creditIncomingTransfer",
        "summary": "Report the credit of a transfer",
        "description": "The beneficiary (KulOffice) reports that it credited a transfer confirmed final, under its own `credit_reference`; the transfer becomes `credited_notified` (`incoming_transfer.credited`). The same reference again changes nothing. A matched transfer that is not final is `409 not_final`; one in compliance review or returned is `409 invalid_state`. Requires `transactions:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "credit_reference"
                ],
                "properties": {
                  "credit_reference": {
                    "type": "string",
                    "maxLength": 128
                  }
                }
              },
              "example": {
                "credit_reference": "KO-TX-9F2A"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The transfer, credited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingTransfer"
                },
                "example": {
                  "id": "3KdW1pZ0incomingTransfer001",
                  "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                  "partner_reference": "BCI-TRF-20261011-0042",
                  "mode": "live",
                  "status": "credited_notified",
                  "amount_minor": 125000,
                  "currency": "MZN",
                  "destination": {
                    "type": "deposit_reference",
                    "value": "INV-1001"
                  },
                  "value_date": "2026-10-11",
                  "final": true,
                  "creditable": false,
                  "beneficiary_name": "L*** D*** L**",
                  "version": 4,
                  "received_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:46:10Z",
                  "final_at": "2026-10-11T09:45:00Z",
                  "beneficiary_account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "credit_reference": "KO-TX-9F2A",
                  "credited_at": "2026-10-11T09:46:10Z"
                }
              }
            }
          },
          "404": {
            "description": "Unknown, or not the caller's (partner or beneficiary) in this mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such incoming transfer",
                  "error": {
                    "code": "not_found",
                    "message": "no such incoming transfer"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Not confirmed final (`not_final`), or a state that cannot be credited (`invalid_state`).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "not_final",
                  "detail": "payments: the transfer is not confirmed final and cannot be credited",
                  "error": {
                    "code": "not_final",
                    "message": "payments: the transfer is not confirmed final and cannot be credited"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/incoming-transfers": {
      "get": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "adminListIncomingTransfers",
        "summary": "Every incoming transfer",
        "description": "For operators, by `status` (e.g. `unmatched`: the money in suspense). Requires `reconciliation: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"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KdW1pZ0incomingTransfer001",
                      "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                      "partner_account_id": "3KbHx5Qp7eYLffSWnjTF10m1Acc",
                      "mode": "live",
                      "partner_reference": "BCI-TRF-20261011-0042",
                      "amount_minor": 125000,
                      "currency": "MZN",
                      "destination_type": "alias",
                      "destination_value": "LOJA-404",
                      "final": true,
                      "final_at": "2026-10-11T09:30:00Z",
                      "status": "unmatched",
                      "in_suspense": true,
                      "reason": "no KulPay account has this alias",
                      "version": 2,
                      "received_at": "2026-10-11T09:30:00Z",
                      "updated_at": "2026-10-11T09:30:01Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/incoming-transfers/{id}/resolutions": {
      "post": {
        "tags": [
          "Incoming transfers"
        ],
        "operationId": "resolveIncomingTransfer",
        "summary": "Resolve an incoming transfer",
        "description": "A typed operator action, never an edit. `compliance_review` (from received, matched, unmatched or confirmed_final) holds the transfer at once: `200 {transfer}`. `bind` (to `account_id`, from unmatched or compliance review), `release` (back to its beneficiary, from compliance review) and `return` (to its source, from unmatched or compliance review) wait for a second person: `202 {approval}`, decided with `POST /v1/admin/approvals/{id}/decide`, and applied only if the transfer has not changed since. A reason is always 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": [
                      "bind",
                      "release",
                      "return",
                      "compliance_review"
                    ]
                  },
                  "account_id": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "bind",
                "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                "reason": "The customer's deposit tag was mistyped (LOJA-404 for LOJA-04)"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approval": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "incoming_transfer.resolve",
                    "subject_id": "3KdW1pZ0incomingTransfer001",
                    "tenant_id": "3KbHx5Qp7eYLffSWnjTF10m1Acc",
                    "payload": "{\"action\":\"bind\",\"account_id\":\"3Ky2WEM0zMfI2pD5i2iBhSEQcB2\",\"version\":2}",
                    "reason": "The customer's deposit tag was mistyped (LOJA-404 for LOJA-04)",
                    "prepared_by": "operator:ana",
                    "status": "pending",
                    "created_at": "2026-10-11T10:02:00Z"
                  }
                }
              }
            }
          },
          "200": {
            "description": "Held for compliance review: `{transfer}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transfer": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "transfer": {
                    "id": "3KdW1pZ0incomingTransfer001",
                    "psp_id": "3KbHx5Qp7eYLffSWnjTF10m1LdZ",
                    "partner_account_id": "3KbHx5Qp7eYLffSWnjTF10m1Acc",
                    "mode": "live",
                    "partner_reference": "BCI-TRF-20261011-0042",
                    "amount_minor": 125000,
                    "currency": "MZN",
                    "destination_type": "alias",
                    "destination_value": "LOJA-404",
                    "final": true,
                    "final_at": "2026-10-11T09:30:00Z",
                    "status": "compliance_review",
                    "in_suspense": true,
                    "reason": "Originator on a watch list",
                    "version": 3,
                    "received_at": "2026-10-11T09:30:00Z",
                    "updated_at": "2026-10-11T09:30:01Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Malformed: unknown action, no reason, no such account.",
            "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, or not the caller's (partner or beneficiary) in this mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such incoming transfer",
                  "error": {
                    "code": "not_found",
                    "message": "no such incoming transfer"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The transfer's state does not allow the action, or a decision is already waiting.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "invalid_state",
                  "detail": "only an unmatched transfer or one in compliance review can be bound or returned",
                  "error": {
                    "code": "invalid_state",
                    "message": "only an unmatched transfer or one in compliance review can be bound or returned"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/merchant-applications": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "createMerchantApplication",
        "summary": "Create a merchant application",
        "description": "A merchant's API client in one mode (phase 6). API keys join one on issue (`application_id` on POST /v1/api-keys); a request is the application's of the key that made it, and connector binding allowlists name applications. Idempotent by `external_ref`: the same reference returns the existing application with 200. Records `config.merchant_application.created`. Requires `keys:manage` or `connectors:write`; `account_id` of another account needs `accounts:write`. IG-76 (owner decision 2026-10-12, \"narrow service scopes\"): a trusted service (KulOffice) acts for a merchant with `account_id` and an explicit `mode`, holding the operator-issued `service:applications:write` (platform, operator-only: `keys:manage` cannot grant it, a merchant key never holds it). The scope reaches another account only; on the caller's own account the merchant scopes apply. A service key never acts above its own mode: a test-mode key provisions test mode only (`403 mode_not_allowed`); a live-mode key may name test or live. With it, `mode` is required (`400 mode_required`) and `account_id` must exist (`404 account_not_found`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "account_id": {
                    "type": "string",
                    "description": "Another account: needs `accounts:write`, or `service:applications:write` (IG-76). Omitted: the caller's own."
                  },
                  "mode": {
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "name": {
                    "type": "string"
                  },
                  "external_ref": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "mode": "live",
                "name": "Loja online",
                "external_ref": "pacapp_01JABCDXYZ"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new application.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "merchant_application"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "external_ref": {
                      "type": "string",
                      "description": "The caller's own id (KulOffice's `pacapp_\u2026`); unique per account and mode."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "object": "merchant_application",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "name": "Loja online",
                  "external_ref": "pacapp_01JABCDXYZ",
                  "status": "active",
                  "created_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "200": {
            "description": "The application with this external_ref, already created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "merchant_application"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "external_ref": {
                      "type": "string",
                      "description": "The caller's own id (KulOffice's `pacapp_\u2026`); unique per account and mode."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "object": "merchant_application",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "name": "Loja online",
                  "external_ref": "pacapp_01JABCDXYZ",
                  "status": "active",
                  "created_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "An application needs a name of at most 255 characters.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "An application needs a name of at most 255 characters.",
                  "error": {
                    "code": "invalid_request",
                    "message": "An application needs a name of at most 255 characters."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Another account without `accounts:write` or `service:applications:write`, the service's own account without a merchant scope (`insufficient_scope`), or `mode_not_allowed`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:applications:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:applications:write"
                  },
                  "missing_scope": "service:applications:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: `account_id` names no account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account with id 3KmissingAccount000000000001 exists on this deployment.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account with id 3KmissingAccount000000000001 exists on this deployment."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "listMerchantApplications",
        "summary": "List merchant applications",
        "description": "The account's applications, oldest first. Requires `keys:manage`, `connectors:read` or `admin:read`. Or `service:applications:write` with `account_id` (IG-76): a test-mode service key lists test mode only.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "One mode; omitted: both."
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Another account (needs `accounts:write`, or `service:applications:write`); omitted: the caller's own."
          }
        ],
        "responses": {
          "200": {
            "description": "The applications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "object": {
                            "enum": [
                              "merchant_application"
                            ]
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "enum": [
                              "live",
                              "test"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "external_ref": {
                            "type": "string",
                            "description": "The caller's own id (KulOffice's `pacapp_\u2026`); unique per account and mode."
                          },
                          "status": {
                            "enum": [
                              "active",
                              "disabled"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                      "object": "merchant_application",
                      "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                      "mode": "live",
                      "name": "Loja online",
                      "external_ref": "pacapp_01JABCDXYZ",
                      "status": "active",
                      "created_at": "2026-10-11T09:30:00Z",
                      "updated_at": "2026-10-11T09:30:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/merchant-applications/{id}": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "getMerchantApplication",
        "summary": "Get a merchant application",
        "description": "One application of the account. Requires `keys:manage`, `connectors:read` or `admin:read`. Or `service:applications:write` with `account_id` (IG-76); another account's application, or a live one for a test-mode service key, is not found.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Another account (needs `accounts:write`, or `service:applications:write`); omitted: the caller's own."
          }
        ],
        "responses": {
          "200": {
            "description": "The application.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "merchant_application"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "external_ref": {
                      "type": "string",
                      "description": "The caller's own id (KulOffice's `pacapp_\u2026`); unique per account and mode."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "object": "merchant_application",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "name": "Loja online",
                  "external_ref": "pacapp_01JABCDXYZ",
                  "status": "active",
                  "created_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "updateMerchantApplication",
        "summary": "Rename, disable or enable an application",
        "description": "A disabled application's keys are refused at charge authorization (`403 application_inactive`) before any routing or provider call; enabling it restores them. Records `config.merchant_application.updated`. Requires `keys:manage` or `connectors:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "status": {
                    "enum": [
                      "active",
                      "disabled"
                    ]
                  }
                }
              },
              "example": {
                "status": "disabled"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The application.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "merchant_application"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "external_ref": {
                      "type": "string",
                      "description": "The caller's own id (KulOffice's `pacapp_\u2026`); unique per account and mode."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                  "object": "merchant_application",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "name": "Loja online",
                  "external_ref": "pacapp_01JABCDXYZ",
                  "status": "disabled",
                  "created_at": "2026-10-11T09:30:00Z",
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "description": "status is active or disabled",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "status is active or disabled",
                  "error": {
                    "code": "invalid_request",
                    "message": "status is active or disabled"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/connector-bindings": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "listBindingProjections",
        "summary": "List binding projections",
        "description": "The safe projection of the account's connector bindings, for KulOffice to keep: id, masked label, method, mode, status and allowed applications. No credential, credential field name or last-4 ever appears. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "One mode; omitted: both."
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Another account (needs `accounts:write`); omitted: the caller's own."
          }
        ],
        "responses": {
          "200": {
            "description": "The projections.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "description": "What KulOffice may keep of a binding. Never a credential, a credential field name or a last-4.",
                        "properties": {
                          "binding_id": {
                            "type": "string"
                          },
                          "account_id": {
                            "type": "string"
                          },
                          "mode": {
                            "enum": [
                              "live",
                              "test"
                            ]
                          },
                          "method": {
                            "type": "string"
                          },
                          "driver": {
                            "type": "string"
                          },
                          "masked_label": {
                            "type": "string",
                            "description": "The binding's label with any run of five or more digits reduced to its last four (`Loja \u2022\u2022\u2022\u20222345`); `<driver> <method>` when it has none."
                          },
                          "status": {
                            "enum": [
                              "active",
                              "disabled",
                              "credentials_missing"
                            ]
                          },
                          "allowed_applications": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Merchant applications allowed to use the binding; empty: every application of the account."
                          },
                          "updated_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                      "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                      "mode": "live",
                      "method": "mpesa",
                      "driver": "mpesa",
                      "masked_label": "Loja M-Pesa \u2022\u2022\u2022\u20222345",
                      "status": "active",
                      "allowed_applications": [
                        "3KUXQ43bztiuf9tCMNgbcrYvne3"
                      ],
                      "updated_at": "2026-10-11T09:30:00Z"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/connector-bindings/{id}": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "getBindingProjection",
        "summary": "Get a binding projection",
        "description": "One binding of the account as its safe projection. Requires `connectors:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Another account (needs `accounts:write`); omitted: the caller's own."
          }
        ],
        "responses": {
          "200": {
            "description": "The projection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "What KulOffice may keep of a binding. Never a credential, a credential field name or a last-4.",
                  "properties": {
                    "binding_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "masked_label": {
                      "type": "string",
                      "description": "The binding's label with any run of five or more digits reduced to its last four (`Loja \u2022\u2022\u2022\u20222345`); `<driver> <method>` when it has none."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled",
                        "credentials_missing"
                      ]
                    },
                    "allowed_applications": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Merchant applications allowed to use the binding; empty: every application of the account."
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "method": "mpesa",
                  "driver": "mpesa",
                  "masked_label": "Loja M-Pesa \u2022\u2022\u2022\u20222345",
                  "status": "active",
                  "allowed_applications": [
                    "3KUXQ43bztiuf9tCMNgbcrYvne3"
                  ],
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/connector-bindings/{id}/applications": {
      "put": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "setBindingApplications",
        "summary": "Set a binding's application allowlist",
        "description": "Replaces the allowlist. A request from an application not on it, or from a key in no application, never reaches the binding: it is refused before any provider call with `403 application_not_allowed` (charges, hosted checkout attempts, name lookups, payouts; a routed PSP target that refuses is skipped). `[]` lifts the restriction, which is also the state of every binding before allowlists. Every id must be an application of the binding's account in the binding's mode. Records `config.connector.applications_changed`. Requires `connectors:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "application_ids"
                ],
                "properties": {
                  "account_id": {
                    "type": "string"
                  },
                  "application_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "application_ids": [
                  "3KUXQ43bztiuf9tCMNgbcrYvne3"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The binding's projection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "What KulOffice may keep of a binding. Never a credential, a credential field name or a last-4.",
                  "properties": {
                    "binding_id": {
                      "type": "string"
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "masked_label": {
                      "type": "string",
                      "description": "The binding's label with any run of five or more digits reduced to its last four (`Loja \u2022\u2022\u2022\u20222345`); `<driver> <method>` when it has none."
                    },
                    "status": {
                      "enum": [
                        "active",
                        "disabled",
                        "credentials_missing"
                      ]
                    },
                    "allowed_applications": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Merchant applications allowed to use the binding; empty: every application of the account."
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "method": "mpesa",
                  "driver": "mpesa",
                  "masked_label": "Loja M-Pesa \u2022\u2022\u2022\u20222345",
                  "status": "active",
                  "allowed_applications": [
                    "3KUXQ43bztiuf9tCMNgbcrYvne3"
                  ],
                  "updated_at": "2026-10-11T09:30:00Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "400": {
            "description": "application 3KUXQ5Hc8Hb0Dk1vJv3m2Qp7xYz is not an application of this account in live mode",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "application 3KUXQ5Hc8Hb0Dk1vJv3m2Qp7xYz is not an application of this account in live mode",
                  "error": {
                    "code": "invalid_request",
                    "message": "application 3KUXQ5Hc8Hb0Dk1vJv3m2Qp7xYz is not an application of this account in live mode"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/connector-sessions": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "createConnectorSession",
        "summary": "Open a secure connector-configuration session",
        "description": "Opens a short-lived (`ttl_seconds`, 60 to 3600, default 900), single-use session through which the merchant submits its own provider credentials straight to the Gateway: send the merchant to `url` (or embed it), or post them to POST /v1/connector-sessions/submit with `token`. The token is returned once and kept only as a hash. On submission the credentials are checked (every `fields` entry present, the driver builds a connector with them), envelope-encrypted onto the binding for (account, mode, method), created or rotated, and `allowed_application_ids`, when given, becomes its allowlist. Records `credential.connector_session.created`; no event or response carries a credential. Requires `connectors:write`; `account_id` of another account needs `accounts:write`. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret shown once and is never kept: the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`. IG-76 (owner decision 2026-10-12, \"narrow service scopes\"): a trusted service (KulOffice) acts for a merchant with `account_id` and an explicit `mode`, holding the operator-issued `service:connector-sessions:write` (platform, operator-only: `keys:manage` cannot grant it, a merchant key never holds it). The scope reaches another account only; on the caller's own account the merchant scopes apply. A service key never acts above its own mode: a test-mode key provisions test mode only (`403 mode_not_allowed`); a live-mode key may name test or live. With it, `mode` is required (`400 mode_required`), and each of `allowed_application_ids` must be the account's in that mode (`404 application_not_found`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "method",
                  "driver"
                ],
                "properties": {
                  "account_id": {
                    "type": "string",
                    "description": "Another account: needs `accounts:write`, or `service:connector-sessions:write` (IG-76). Omitted: the caller's own."
                  },
                  "mode": {
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "method": {
                    "type": "string"
                  },
                  "driver": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "allowed_application_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "ttl_seconds": {
                    "type": "integer"
                  }
                }
              },
              "example": {
                "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                "mode": "live",
                "method": "mpesa",
                "driver": "mpesa",
                "label": "Loja M-Pesa 8812345",
                "fields": [
                  "api_key",
                  "public_key",
                  "service_provider_code"
                ],
                "allowed_application_ids": [
                  "3KUXQ43bztiuf9tCMNgbcrYvne3"
                ],
                "ttl_seconds": 900
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The session, with its one-time token and hosted URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "connector_session"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Credential field names the hosted page asks for; empty: one JSON object."
                    },
                    "allowed_applications": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Set on the binding on completion, when the session was opened with `allowed_application_ids`."
                    },
                    "status": {
                      "enum": [
                        "open",
                        "completed",
                        "expired"
                      ]
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "binding_id": {
                      "type": "string",
                      "description": "The binding the credentials were vaulted on, once completed."
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "token": {
                      "type": "string",
                      "description": "The one-time token: on creation only, never stored (a SHA-256 hash is)."
                    },
                    "url": {
                      "type": "string",
                      "description": "The Gateway-hosted page for the merchant, with the token in the query string (never logged). On creation only."
                    }
                  }
                },
                "example": {
                  "id": "3KUXR7nM4kQp2Xc9VbT1wYz5LhG",
                  "object": "connector_session",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "method": "mpesa",
                  "driver": "mpesa",
                  "label": "Loja M-Pesa 8812345",
                  "fields": [
                    "api_key",
                    "public_key",
                    "service_provider_code"
                  ],
                  "allowed_applications": [
                    "3KUXQ43bztiuf9tCMNgbcrYvne3"
                  ],
                  "status": "open",
                  "expires_at": "2026-10-11T09:45:00Z",
                  "created_at": "2026-10-11T09:30:00Z",
                  "token": "kcs_Q2hhbmdlTWVJbkV4YW1wbGVzT25seV9ub3RfYV9yZWFsX3Rva2Vu",
                  "url": "https://pay.kulpay.co.mz/connector-setup?token=kcs_Q2hhbmdlTWVJbkV4YW1wbGVzT25seV9ub3RfYV9yZWFsX3Rva2Vu"
                }
              }
            }
          },
          "400": {
            "description": "a session lasts between 1 and 60 minutes",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "a session lasts between 1 and 60 minutes",
                  "error": {
                    "code": "invalid_request",
                    "message": "a session lasts between 1 and 60 minutes"
                  }
                }
              }
            }
          },
          "501": {
            "description": "This deployment was started without a connector factory, so bindings cannot be managed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Connector management unavailable",
                  "status": 501,
                  "code": "connectors_disabled",
                  "detail": "This deployment was started without a connector factory, so bindings cannot be managed.",
                  "error": {
                    "code": "connectors_disabled",
                    "message": "This deployment was started without a connector factory, so bindings cannot be managed."
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          },
          "403": {
            "description": "Another account without `accounts:write` or `service:connector-sessions:write`, or `mode_not_allowed`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:connector-sessions:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:connector-sessions:write"
                  },
                  "missing_scope": "service:connector-sessions:write"
                }
              }
            }
          },
          "404": {
            "description": "`application_not_found`: the application is not the account's in this mode (another account's is not found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Application not found",
                  "status": 404,
                  "code": "application_not_found",
                  "detail": "No application with this id belongs to the account in this mode.",
                  "error": {
                    "code": "application_not_found",
                    "message": "No application with this id belongs to the account in this mode."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/connector-sessions/{id}": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "getConnectorSession",
        "summary": "Get a connector-configuration session",
        "description": "The session's status (`open`, `completed`, `expired`) and, once completed, the binding the credentials are on. Never the token. Requires `connectors:read`. Or `service:connector-sessions:write` with `account_id` (IG-76); a live session is not found for a test-mode service key.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Another account (needs `accounts:write`); omitted: the caller's own."
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "connector_session"
                      ]
                    },
                    "account_id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "method": {
                      "type": "string"
                    },
                    "driver": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Credential field names the hosted page asks for; empty: one JSON object."
                    },
                    "allowed_applications": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Set on the binding on completion, when the session was opened with `allowed_application_ids`."
                    },
                    "status": {
                      "enum": [
                        "open",
                        "completed",
                        "expired"
                      ]
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "binding_id": {
                      "type": "string",
                      "description": "The binding the credentials were vaulted on, once completed."
                    },
                    "completed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "token": {
                      "type": "string",
                      "description": "The one-time token: on creation only, never stored (a SHA-256 hash is)."
                    },
                    "url": {
                      "type": "string",
                      "description": "The Gateway-hosted page for the merchant, with the token in the query string (never logged). On creation only."
                    }
                  }
                },
                "example": {
                  "id": "3KUXR7nM4kQp2Xc9VbT1wYz5LhG",
                  "object": "connector_session",
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "mode": "live",
                  "method": "mpesa",
                  "driver": "mpesa",
                  "label": "Loja M-Pesa 8812345",
                  "fields": [
                    "api_key",
                    "public_key",
                    "service_provider_code"
                  ],
                  "allowed_applications": [
                    "3KUXQ43bztiuf9tCMNgbcrYvne3"
                  ],
                  "status": "completed",
                  "expires_at": "2026-10-11T09:45:00Z",
                  "created_at": "2026-10-11T09:30:00Z",
                  "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                  "completed_at": "2026-10-11T09:33:12Z"
                }
              }
            }
          },
          "404": {
            "description": "No connector configuration session has this token or id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Session not found",
                  "status": 404,
                  "code": "connector_session_not_found",
                  "detail": "No connector configuration session has this token or id.",
                  "error": {
                    "code": "connector_session_not_found",
                    "message": "No connector configuration session has this token or id."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/connector-sessions/submit": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "submitConnectorSession",
        "summary": "Submit credentials to a session",
        "security": [],
        "description": "No API key: the one-time `token` is the capability. `config` is the driver's credential object. The credentials are validated, envelope-encrypted onto the binding and the session consumed in one transaction; a second submission (`409 connector_session_used`), one after expiry (`410 connector_session_expired`) or one that does not build a connector (`400 invalid_connector`) stores nothing and records `credential.connector_session.refused`. The answer is the binding's projection; the credentials are never echoed. The same submission is the form on GET /connector-setup.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "config"
                ],
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "token": "kcs_Q2hhbmdlTWVJbkV4YW1wbGVzT25seV9ub3RfYV9yZWFsX3Rva2Vu",
                "config": {
                  "api_key": "<your M-Pesa API key>",
                  "public_key": "<your public key>",
                  "service_provider_code": "<your code>"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The session and the binding's projection.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "object": {
                          "enum": [
                            "connector_session"
                          ]
                        },
                        "account_id": {
                          "type": "string"
                        },
                        "mode": {
                          "enum": [
                            "live",
                            "test"
                          ]
                        },
                        "method": {
                          "type": "string"
                        },
                        "driver": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        },
                        "fields": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Credential field names the hosted page asks for; empty: one JSON object."
                        },
                        "allowed_applications": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Set on the binding on completion, when the session was opened with `allowed_application_ids`."
                        },
                        "status": {
                          "enum": [
                            "open",
                            "completed",
                            "expired"
                          ]
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "binding_id": {
                          "type": "string",
                          "description": "The binding the credentials were vaulted on, once completed."
                        },
                        "completed_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "token": {
                          "type": "string",
                          "description": "The one-time token: on creation only, never stored (a SHA-256 hash is)."
                        },
                        "url": {
                          "type": "string",
                          "description": "The Gateway-hosted page for the merchant, with the token in the query string (never logged). On creation only."
                        }
                      }
                    },
                    "binding": {
                      "type": "object",
                      "description": "What KulOffice may keep of a binding. Never a credential, a credential field name or a last-4.",
                      "properties": {
                        "binding_id": {
                          "type": "string"
                        },
                        "account_id": {
                          "type": "string"
                        },
                        "mode": {
                          "enum": [
                            "live",
                            "test"
                          ]
                        },
                        "method": {
                          "type": "string"
                        },
                        "driver": {
                          "type": "string"
                        },
                        "masked_label": {
                          "type": "string",
                          "description": "The binding's label with any run of five or more digits reduced to its last four (`Loja \u2022\u2022\u2022\u20222345`); `<driver> <method>` when it has none."
                        },
                        "status": {
                          "enum": [
                            "active",
                            "disabled",
                            "credentials_missing"
                          ]
                        },
                        "allowed_applications": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Merchant applications allowed to use the binding; empty: every application of the account."
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "session": {
                    "id": "3KUXR7nM4kQp2Xc9VbT1wYz5LhG",
                    "object": "connector_session",
                    "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                    "mode": "live",
                    "method": "mpesa",
                    "driver": "mpesa",
                    "label": "Loja M-Pesa 8812345",
                    "fields": [
                      "api_key",
                      "public_key",
                      "service_provider_code"
                    ],
                    "allowed_applications": [
                      "3KUXQ43bztiuf9tCMNgbcrYvne3"
                    ],
                    "status": "completed",
                    "expires_at": "2026-10-11T09:45:00Z",
                    "created_at": "2026-10-11T09:30:00Z",
                    "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                    "completed_at": "2026-10-11T09:33:12Z"
                  },
                  "binding": {
                    "binding_id": "3KUXR0yb2Wq9vLz1PcDq8sT4nAe",
                    "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                    "mode": "live",
                    "method": "mpesa",
                    "driver": "mpesa",
                    "masked_label": "Loja M-Pesa \u2022\u2022\u2022\u20222345",
                    "status": "active",
                    "allowed_applications": [
                      "3KUXQ43bztiuf9tCMNgbcrYvne3"
                    ],
                    "updated_at": "2026-10-11T09:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The driver is unknown, or its configuration is missing/malformed. The binding was not saved.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid connector configuration",
                  "status": 400,
                  "code": "invalid_connector",
                  "detail": "The driver is unknown, or its configuration is missing/malformed. The binding was not saved.",
                  "error": {
                    "code": "invalid_connector",
                    "message": "The driver is unknown, or its configuration is missing/malformed. The binding was not saved."
                  }
                }
              }
            }
          },
          "404": {
            "description": "No connector configuration session has this token or id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Session not found",
                  "status": 404,
                  "code": "connector_session_not_found",
                  "detail": "No connector configuration session has this token or id.",
                  "error": {
                    "code": "connector_session_not_found",
                    "message": "No connector configuration session has this token or id."
                  }
                }
              }
            }
          },
          "409": {
            "description": "This configuration session was already used. Ask for a new one.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Session already used",
                  "status": 409,
                  "code": "connector_session_used",
                  "detail": "This configuration session was already used. Ask for a new one.",
                  "error": {
                    "code": "connector_session_used",
                    "message": "This configuration session was already used. Ask for a new one."
                  }
                }
              }
            }
          },
          "410": {
            "description": "This configuration session has expired. Ask for a new one.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Session expired",
                  "status": 410,
                  "code": "connector_session_expired",
                  "detail": "This configuration session has expired. Ask for a new one.",
                  "error": {
                    "code": "connector_session_expired",
                    "message": "This configuration session has expired. Ask for a new one."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/connector-setup": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "showConnectorSetup",
        "summary": "The hosted credentials page",
        "security": [],
        "description": "The Gateway-hosted page a merchant opens from a session's `url` (it may be embedded where `frame_ancestors` allows). The token rides in the query string, which is never logged, then in a hidden field. A used, expired or unknown session shows no form.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The form, or the session's state.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "postConnectorSetup",
        "summary": "Submit the hosted credentials form",
        "security": [],
        "description": "The form's submission: `token`, and each requested field as `field_<name>` (or one JSON object in `config`). CSRF-protected when CSRF is enabled. Same rules as POST /v1/connector-sessions/submit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string"
                  },
                  "config": {
                    "type": "string"
                  },
                  "_csrf": {
                    "type": "string"
                  }
                },
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credentials stored.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "A field is missing or the provider configuration was refused; the form again.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown session.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "409": {
            "description": "The session was already used.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "410": {
            "description": "The session expired.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/settlement-arrangements": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getMyArrangements",
        "summary": "The caller's settlement arrangements",
        "description": "The versions in force (one per currency) and those waiting for a checker. The destination is shown masked. Requires `settlements:read`. Plan phase 8: managed pass-through money is never in the available balance and nothing here executes a payout.",
        "responses": {
          "200": {
            "description": "`{account_id, active, pending}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "active": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SettlementArrangement"
                      }
                    },
                    "pending": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SettlementArrangement"
                      }
                    }
                  }
                },
                "example": {
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "active": [
                    {
                      "id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "currency": "MZN",
                      "version": 2,
                      "status": "active",
                      "custody": "managed_pass_through",
                      "destination_ref": "*******************0001",
                      "schedule": "daily",
                      "available_after_days": 1,
                      "minimum_settlement_minor": 100000,
                      "reserve_bps": 500,
                      "reserve_hold_days": 7,
                      "platform_fee_bps": 0,
                      "platform_fee_fixed_minor": 0,
                      "provider_cost_bps": 100,
                      "provider_cost_fixed_minor": 0,
                      "refund_fee_policy": "retain",
                      "refund_offset_policy": "offset_future_settlements",
                      "reason": "Adenda de cobran\u00e7a gerida assinada",
                      "created_by": "operator:ana",
                      "approval_id": "3LBv0ZkS1yQd9mVhfN2pWc8rXo4",
                      "approved_by": "operator:rui",
                      "created_at": "2026-10-11T09:12:03Z",
                      "activated_at": "2026-10-11T10:40:17Z"
                    }
                  ],
                  "pending": []
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/payables": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listMyPayables",
        "summary": "The caller's merchant payables",
        "description": "What the merchant is owed for each managed pass-through payment: gross less the merchant-borne platform fee (7% in the managed-collection example), the provider cost and the reserve, plus adjustments, in minor units. States: provisional, available, reserved_for_settlement (in a ready batch), settled, disputed, reversed. A payment can be succeeded while its payable is still pending settlement. Requires `settlements:read`. Plan phase 8: managed pass-through money is never in the available balance and nothing here executes a payout.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "live or test; the key's mode when omitted.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "A payable state.",
            "schema": {
              "type": "string",
              "enum": [
                "provisional",
                "available",
                "reserved_for_settlement",
                "settled",
                "disputed",
                "reversed"
              ]
            }
          },
          {
            "name": "batch_id",
            "in": "query",
            "required": false,
            "description": "A settlement batch.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total, summary}`: the payables, oldest first, and per state the count and net.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantPayable"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "summary": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "state": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "mode": "live",
                      "currency": "MZN",
                      "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                      "arrangement_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                      "arrangement_version": 2,
                      "gross_minor": 12345,
                      "platform_fee_minor": 864,
                      "provider_cost_minor": 123,
                      "reserve_minor": 617,
                      "adjustments_minor": 0,
                      "net_minor": 10741,
                      "refunded_minor": 0,
                      "fee_refunded_minor": 0,
                      "state": "reserved_for_settlement",
                      "available_at": "2026-10-12T08:15:00Z",
                      "reserve_release_at": "2026-10-18T08:15:00Z",
                      "batch_id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                      "created_at": "2026-10-11T08:15:00Z",
                      "updated_at": "2026-10-12T09:00:00Z"
                    }
                  ],
                  "total": 1,
                  "summary": [
                    {
                      "state": "provisional",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "available",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "reserved_for_settlement",
                      "count": 1,
                      "net_minor": 10741
                    },
                    {
                      "state": "settled",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "disputed",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "reversed",
                      "count": 0,
                      "net_minor": 0
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/payables/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getMyPayable",
        "summary": "A merchant payable",
        "description": "One of the caller's payables with its adjustments and receivables. Requires `settlements:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payable with `adjustments` (refunds, fee refunds, reversals before it was batched) and `receivables` (what the merchant owes back for it).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/MerchantPayable"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "adjustments": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PayableAdjustment"
                          }
                        },
                        "receivables": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MerchantReceivable"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "mode": "live",
                  "currency": "MZN",
                  "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                  "arrangement_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                  "arrangement_version": 2,
                  "gross_minor": 12345,
                  "platform_fee_minor": 864,
                  "provider_cost_minor": 123,
                  "reserve_minor": 617,
                  "adjustments_minor": 0,
                  "net_minor": 10741,
                  "refunded_minor": 0,
                  "fee_refunded_minor": 0,
                  "state": "reserved_for_settlement",
                  "available_at": "2026-10-12T08:15:00Z",
                  "reserve_release_at": "2026-10-18T08:15:00Z",
                  "batch_id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                  "created_at": "2026-10-11T08:15:00Z",
                  "updated_at": "2026-10-12T09:00:00Z",
                  "adjustments": [],
                  "receivables": []
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/receivables": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listMyReceivables",
        "summary": "What the caller owes back",
        "description": "Managed pass-through money the merchant owes: a refund or reversal after its payable was batched or settled, or the retained fee and cost of a payment refunded beyond its payable. Never a negative balance: `offset_future_settlements` receivables are deducted from later batches, `invoice` ones wait for a KulOffice service invoice. Requires `settlements:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "live or test; the key's mode when omitted.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "open or offset.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "offset"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total, outstanding_minor}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantReceivable"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "outstanding_minor": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3LBvE2pK9wYq1nTb6mRcFs3xZd7",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "mode": "live",
                      "currency": "MZN",
                      "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                      "payable_id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                      "kind": "refund_after_settlement",
                      "source_id": "3LBvF8nQ4rTz2kWm7pXcHd5sYe1",
                      "amount_minor": 37200,
                      "outstanding_minor": 37200,
                      "policy": "offset_future_settlements",
                      "status": "open",
                      "created_at": "2026-10-13T14:02:11Z",
                      "updated_at": "2026-10-13T14:02:11Z"
                    }
                  ],
                  "total": 1,
                  "outstanding_minor": 37200
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/settlement-arrangements": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "listArrangements",
        "summary": "A merchant's settlement arrangement versions",
        "description": "Every version, newest first; `currency` narrows to one. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 code.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SettlementArrangement"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "currency": "MZN",
                      "version": 2,
                      "status": "active",
                      "custody": "managed_pass_through",
                      "destination_ref": "*******************0001",
                      "schedule": "daily",
                      "available_after_days": 1,
                      "minimum_settlement_minor": 100000,
                      "reserve_bps": 500,
                      "reserve_hold_days": 7,
                      "platform_fee_bps": 0,
                      "platform_fee_fixed_minor": 0,
                      "provider_cost_bps": 100,
                      "provider_cost_fixed_minor": 0,
                      "refund_fee_policy": "retain",
                      "refund_offset_policy": "offset_future_settlements",
                      "reason": "Adenda de cobran\u00e7a gerida assinada",
                      "created_by": "operator:ana",
                      "approval_id": "3LBv0ZkS1yQd9mVhfN2pWc8rXo4",
                      "approved_by": "operator:rui",
                      "created_at": "2026-10-11T09:12:03Z",
                      "activated_at": "2026-10-11T10:40:17Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "proposeArrangement",
        "summary": "Propose a settlement arrangement version",
        "description": "A new version of the merchant's arrangement in a currency: custody, the approved destination's identifier, schedule, availability delay, minimum, reserve, merchant-borne platform fee and provider cost (basis points of the gross plus a fixed amount, each rounded half up), the refund fee policy and the refund-offset policy. It waits for a second person (`POST /v1/admin/approvals/{id}/decide`, kind `settlement_arrangement.activate`); once active it applies to new payments only, each payment recording the version it was routed under. managed_pass_through makes a collecting payment `managed` (a merchant payable); merchant_direct makes it direct. KulPay-balance payments stay internal. Requires `accounts:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "currency": {
                    "type": "string"
                  },
                  "custody": {
                    "type": "string",
                    "enum": [
                      "managed_pass_through",
                      "merchant_direct"
                    ]
                  },
                  "destination_ref": {
                    "type": "string",
                    "description": "The identifier of a destination approved in KulOffice's register (4 to 64 letters, digits, `_:.-`), never an account number or MSISDN. Required for managed_pass_through; not allowed for merchant_direct."
                  },
                  "schedule": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "weekly",
                      "manual"
                    ]
                  },
                  "schedule_weekday": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 7
                  },
                  "available_after_days": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 30
                  },
                  "minimum_settlement_minor": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "reserve_bps": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10000
                  },
                  "reserve_hold_days": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 365
                  },
                  "platform_fee_bps": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 0,
                    "description": "No longer used (IG-29, owner decision 2026-10-12): the platform fee is the one KulOffice captured, sent as each payment's fee instruction. A new version must leave it 0."
                  },
                  "platform_fee_fixed_minor": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 0,
                    "description": "No longer used (IG-29, owner decision 2026-10-12): the platform fee is the one KulOffice captured, sent as each payment's fee instruction. A new version must leave it 0."
                  },
                  "provider_cost_bps": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10000
                  },
                  "provider_cost_fixed_minor": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "refund_fee_policy": {
                    "type": "string",
                    "enum": [
                      "retain",
                      "proportional"
                    ]
                  },
                  "refund_offset_policy": {
                    "type": "string",
                    "enum": [
                      "offset_future_settlements",
                      "invoice"
                    ]
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "required": [
                  "currency",
                  "custody",
                  "reason"
                ]
              },
              "example": {
                "currency": "MZN",
                "custody": "managed_pass_through",
                "destination_ref": "dst_mpesa_merchant_0001",
                "schedule": "daily",
                "available_after_days": 1,
                "minimum_settlement_minor": 100000,
                "reserve_bps": 500,
                "reserve_hold_days": 7,
                "provider_cost_bps": 100,
                "refund_fee_policy": "retain",
                "refund_offset_policy": "offset_future_settlements",
                "reason": "Adenda de cobran\u00e7a gerida assinada"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: `{arrangement, approval}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "arrangement": {
                      "$ref": "#/components/schemas/SettlementArrangement"
                    },
                    "approval": {
                      "$ref": "#/components/schemas/ApprovalRequest"
                    }
                  }
                },
                "example": {
                  "arrangement": {
                    "id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                    "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "currency": "MZN",
                    "version": 2,
                    "status": "pending",
                    "custody": "managed_pass_through",
                    "destination_ref": "*******************0001",
                    "schedule": "daily",
                    "available_after_days": 1,
                    "minimum_settlement_minor": 100000,
                    "reserve_bps": 500,
                    "reserve_hold_days": 7,
                    "platform_fee_bps": 0,
                    "platform_fee_fixed_minor": 0,
                    "provider_cost_bps": 100,
                    "provider_cost_fixed_minor": 0,
                    "refund_fee_policy": "retain",
                    "refund_offset_policy": "offset_future_settlements",
                    "reason": "Adenda de cobran\u00e7a gerida assinada",
                    "created_by": "operator:ana",
                    "approval_id": "3LBv0ZkS1yQd9mVhfN2pWc8rXo4",
                    "created_at": "2026-10-11T09:12:03Z"
                  },
                  "approval": {
                    "id": "3LBv0ZkS1yQd9mVhfN2pWc8rXo4",
                    "kind": "settlement_arrangement.activate",
                    "subject_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                    "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                    "payload": "{\"arrangement_id\":\"3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz\",\"currency\":\"MZN\",\"custody\":\"managed_pass_through\",\"version\":2}",
                    "reason": "Adenda de cobran\u00e7a gerida assinada",
                    "prepared_by": "operator:ana",
                    "status": "pending",
                    "created_at": "2026-10-11T09:12:03Z"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/settlement-arrangements/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "getArrangement",
        "summary": "A settlement arrangement version",
        "description": "One version, the destination masked. Requires `accounts:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The version.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SettlementArrangement"
                },
                "example": {
                  "id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "currency": "MZN",
                  "version": 2,
                  "status": "active",
                  "custody": "managed_pass_through",
                  "destination_ref": "*******************0001",
                  "schedule": "daily",
                  "available_after_days": 1,
                  "minimum_settlement_minor": 100000,
                  "reserve_bps": 500,
                  "reserve_hold_days": 7,
                  "platform_fee_bps": 0,
                  "platform_fee_fixed_minor": 0,
                  "provider_cost_bps": 100,
                  "provider_cost_fixed_minor": 0,
                  "refund_fee_policy": "retain",
                  "refund_offset_policy": "offset_future_settlements",
                  "reason": "Adenda de cobran\u00e7a gerida assinada",
                  "created_by": "operator:ana",
                  "approval_id": "3LBv0ZkS1yQd9mVhfN2pWc8rXo4",
                  "approved_by": "operator:rui",
                  "created_at": "2026-10-11T09:12:03Z",
                  "activated_at": "2026-10-11T10:40:17Z"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/payables": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "adminListPayables",
        "summary": "A merchant's payables",
        "description": "As `GET /v1/payables`, for any merchant. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "live or test; the key's mode when omitted.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "description": "A payable state.",
            "schema": {
              "type": "string",
              "enum": [
                "provisional",
                "available",
                "reserved_for_settlement",
                "settled",
                "disputed",
                "reversed"
              ]
            }
          },
          {
            "name": "batch_id",
            "in": "query",
            "required": false,
            "description": "A settlement batch.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total, summary}`: the payables, oldest first, and per state the count and net.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantPayable"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "summary": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "state": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          },
                          "net_minor": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "mode": "live",
                      "currency": "MZN",
                      "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                      "arrangement_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                      "arrangement_version": 2,
                      "gross_minor": 12345,
                      "platform_fee_minor": 864,
                      "provider_cost_minor": 123,
                      "reserve_minor": 617,
                      "adjustments_minor": 0,
                      "net_minor": 10741,
                      "refunded_minor": 0,
                      "fee_refunded_minor": 0,
                      "state": "reserved_for_settlement",
                      "available_at": "2026-10-12T08:15:00Z",
                      "reserve_release_at": "2026-10-18T08:15:00Z",
                      "batch_id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                      "created_at": "2026-10-11T08:15:00Z",
                      "updated_at": "2026-10-12T09:00:00Z"
                    }
                  ],
                  "total": 1,
                  "summary": [
                    {
                      "state": "provisional",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "available",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "reserved_for_settlement",
                      "count": 1,
                      "net_minor": 10741
                    },
                    {
                      "state": "settled",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "disputed",
                      "count": 0,
                      "net_minor": 0
                    },
                    {
                      "state": "reversed",
                      "count": 0,
                      "net_minor": 0
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payables/{id}": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "adminGetPayable",
        "summary": "A merchant payable",
        "description": "Any merchant's payable with its adjustments and receivables. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payable with `adjustments` (refunds, fee refunds, reversals before it was batched) and `receivables` (what the merchant owes back for it).",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/MerchantPayable"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "adjustments": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/PayableAdjustment"
                          }
                        },
                        "receivables": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/MerchantReceivable"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "mode": "live",
                  "currency": "MZN",
                  "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                  "arrangement_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                  "arrangement_version": 2,
                  "gross_minor": 12345,
                  "platform_fee_minor": 864,
                  "provider_cost_minor": 123,
                  "reserve_minor": 617,
                  "adjustments_minor": 0,
                  "net_minor": 10741,
                  "refunded_minor": 0,
                  "fee_refunded_minor": 0,
                  "state": "reserved_for_settlement",
                  "available_at": "2026-10-12T08:15:00Z",
                  "reserve_release_at": "2026-10-18T08:15:00Z",
                  "batch_id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                  "created_at": "2026-10-11T08:15:00Z",
                  "updated_at": "2026-10-12T09:00:00Z",
                  "adjustments": [],
                  "receivables": []
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/receivables": {
      "get": {
        "tags": [
          "Ledger"
        ],
        "operationId": "adminListReceivables",
        "summary": "What a merchant owes back",
        "description": "As `GET /v1/receivables`, for any merchant. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "live or test; the key's mode when omitted.",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "ISO 4217 code.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "open or offset.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "offset"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total, outstanding_minor}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/MerchantReceivable"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "outstanding_minor": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3LBvE2pK9wYq1nTb6mRcFs3xZd7",
                      "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                      "mode": "live",
                      "currency": "MZN",
                      "payment_id": "3LBv9qH2sRk8wZcP0uYfTa6eNm1",
                      "payable_id": "3LBvA1cU7mUe4pTnqR5dKx2wYb9",
                      "kind": "refund_after_settlement",
                      "source_id": "3LBvF8nQ4rTz2kWm7pXcHd5sYe1",
                      "amount_minor": 37200,
                      "outstanding_minor": 37200,
                      "policy": "offset_future_settlements",
                      "status": "open",
                      "created_at": "2026-10-13T14:02:11Z",
                      "updated_at": "2026-10-13T14:02:11Z"
                    }
                  ],
                  "total": 1,
                  "outstanding_minor": 37200
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/accounts/{id}/managed-settlements": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "buildManagedSettlement",
        "summary": "Build a managed settlement batch now",
        "description": "Builds the merchant's managed pass-through batch for today under its newest managed arrangement, whatever the schedule (the hourly job builds it on scheduled days): provisional payables past their delay become available; the batch takes every available payable with immutable source lines (gross, platform fee, provider cost, reserve and each adjustment), reserves due for release and, under offset_future_settlements, open receivables (never below zero). The batch stops at `ready`: the gateway does not execute it and POST /v1/payouts is unchanged; executing settlement through the arrangement's destination is the next step, pending the owner's decision. One batch per merchant, mode and currency a day. Requires `admin:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "currency": {
                    "type": "string"
                  }
                },
                "required": [
                  "currency"
                ]
              },
              "example": {
                "mode": "live",
                "currency": "MZN"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The ready batch (`GET /v1/settlements/{id}` lists its lines).",
            "content": {
              "application/json": {
                "schema": {
                  "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"
                    },
                    "gross": {
                      "type": "string"
                    },
                    "fees": {
                      "type": "string"
                    },
                    "refunds": {
                      "type": "string"
                    },
                    "adjustments": {
                      "type": "string"
                    },
                    "net": {
                      "type": "string"
                    },
                    "line_count": {
                      "type": "integer"
                    },
                    "status": {
                      "type": "string"
                    },
                    "arrangement_id": {
                      "type": "string"
                    },
                    "arrangement_version": {
                      "type": "integer"
                    },
                    "reserves": {
                      "type": "string"
                    },
                    "created_at": {
                      "type": "string"
                    },
                    "updated_at": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                  "account_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "mode": "live",
                  "currency": "MZN",
                  "funds_flow": "managed",
                  "cycle_date": "2026-10-12",
                  "cycle_days": 1,
                  "period_end": "2026-10-12T09:00:00Z",
                  "gross": "123.45",
                  "fees": "-9.87",
                  "refunds": "0",
                  "adjustments": "0",
                  "net": "107.41",
                  "line_count": 4,
                  "status": "ready",
                  "arrangement_id": "3LBv0Wf4mWq6bLr3Qx1cSdAq7Tz",
                  "arrangement_version": 2,
                  "reserves": "-6.17",
                  "created_at": "2026-10-12T09:00:00Z",
                  "updated_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "409": {
            "description": "`nothing_to_settle`: no available payable or released reserve, or the net is below the arrangement's minimum.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "nothing_to_settle",
                  "detail": "No available payable or released reserve reaches the arrangement's minimum.",
                  "error": {
                    "code": "nothing_to_settle",
                    "message": "No available payable or released reserve reaches the arrangement's minimum."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/settlements/{id}/confirmations": {
      "post": {
        "tags": [
          "Ledger"
        ],
        "operationId": "confirmManagedSettlement",
        "summary": "Record a ready batch settled outside the gateway",
        "description": "For a `ready` managed pass-through batch, or a `ready` collecting batch (collecting auto-payout off, `settlement.collecting_auto_payout`; G14): asks a second person (`POST /v1/admin/approvals/{id}/decide`, kind `settlement.batch.confirm`) to record that it was settled outside the gateway, with the external reference as evidence. One request per batch waits at a time. On approval the batch is `settled`. Managed: its payables are settled and the ledger moves the net from the payable out of provider custody. Collecting: a `settlement_external` ledger entry takes the net out of the merchant's available balance against client funds, as the batch's payout would have. The gateway moves no money. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_reference": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                },
                "required": [
                  "external_reference",
                  "reason"
                ]
              },
              "example": {
                "external_reference": "MPESA-B2C-7781",
                "reason": "Extracto banc\u00e1rio mostra a transfer\u00eancia"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for approval: the approval request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovalRequest"
                },
                "example": {
                  "id": "3LBvH5tW8yRc3mQn1kZpLd6sXf2",
                  "kind": "settlement.batch.confirm",
                  "subject_id": "3LBvC7rM2tXn5qWe8kJdLs4vHg0",
                  "tenant_id": "3KY8VJtMMG2V1cNjNhTgPt1mSFI",
                  "payload": "{\"batch_id\":\"3LBvC7rM2tXn5qWe8kJdLs4vHg0\",\"external_reference\":\"MPESA-B2C-7781\"}",
                  "reason": "Extracto banc\u00e1rio mostra a transfer\u00eancia",
                  "prepared_by": "operator:ana",
                  "status": "pending",
                  "created_at": "2026-10-11T09:12:03Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/InvalidState"
          }
        }
      }
    },
    "/v1/service/payments/{provider_payment_id}": {
      "get": {
        "tags": [
          "Charges"
        ],
        "operationId": "getServicePayment",
        "summary": "A payment, read back by a trusted service",
        "description": "For KulOffice (IG-18): any account's payment by the Gateway's id, in the key's own mode, with the merchant application recorded on the payment and its `external_ref`, the binding, the funds flow, the settlement evidence and (IG-73) the settlement arrangement, launch route and capability recorded when the payment was routed. Requires `service:payments:read`, an operator-issued scope (a service account, or a key issued with `admin:write`); an account cannot grant it to itself. A payment of the other mode is not found.",
        "parameters": [
          {
            "name": "provider_payment_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicePayment"
                },
                "example": {
                  "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                  "payment_id": "ord_1001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "status": "succeeded",
                  "version": 4,
                  "money": {
                    "amount": "250.50",
                    "currency": "MZN"
                  },
                  "amount_minor": 25050,
                  "currency": "MZN",
                  "application_id": "3KfOz1mApplicationWeb00001",
                  "application_external_ref": "pacapp_7Hq2",
                  "connector_binding_id": "3KfOyBindingMpesa000000001",
                  "connector_ref": "mp_ws_CO_11102026093000",
                  "funds_flow": "direct",
                  "settled_at": "2026-10-11T09:30:05.123456789Z",
                  "settled_amount_minor": 25050,
                  "last_updated_at": "2026-10-11T09:30:05.123456789Z"
                }
              }
            }
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:payments:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:payments:read"
                  },
                  "missing_scope": "service:payments:read"
                }
              }
            },
            "description": "The key lacks `service:payments:read` (a merchant key, whatever its scopes), or there is no key."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "payment not found",
                  "error": {
                    "code": "not_found",
                    "message": "payment not found"
                  }
                }
              }
            },
            "description": "No payment with this id in the key's mode."
          }
        }
      }
    },
    "/v1/admin/launch-matrix": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "getLaunchMatrix",
        "summary": "The launch capability matrix",
        "description": "The matrix the gateway enforces at its routing and authorization choke points (plan phase 10 item 9): operation x connector driver x method x mode x movement scope. A route it does not enable is refused before any reservation or provider call (`route_not_enabled`); off_net is refused outright (`movement_scope_disabled`). Payout rows are legacy behaviour kept pending the owner's decision. The default (`2026-10-12.3`) runs the simulated fleet (driver `mock`) in test mode only (owner decision 2026-10-12): a live charge, payout or name lookup that would run on it is `route_not_enabled`. It also lists cash deposits (`cash_deposit`, driver `partner_api`, method `cash`, scope `acc_top`): disabled in live mode, enabled in test mode (IG-52). Check production's connector bindings against this answer before every deploy. The binary runs the embedded default unless `launch_matrix.file` (`KULPAY_LAUNCH_MATRIX_FILE`) names another. Requires `connectors:read`.",
        "responses": {
          "200": {
            "description": "The matrix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LaunchMatrix"
                },
                "example": {
                  "enforced": true,
                  "off_net": "refused",
                  "version": "2026-10-12.1",
                  "description": "Launch capability matrix ...",
                  "rows": [
                    {
                      "operation": "charge",
                      "driver": "kulpay_balance",
                      "methods": [
                        "kulpay_balance"
                      ],
                      "modes": [
                        "live",
                        "test"
                      ],
                      "scopes": [
                        "on_net"
                      ],
                      "enabled": true,
                      "note": "KulPay Balance checkout with internal merchant settlement (phase 4B)."
                    },
                    {
                      "operation": "payout",
                      "driver": "mpesa",
                      "methods": [
                        "mpesa"
                      ],
                      "modes": [
                        "live",
                        "test"
                      ],
                      "scopes": [
                        "legacy_payout"
                      ],
                      "enabled": true,
                      "legacy": true,
                      "owner_decision": "POST /v1/payouts ... pending the owner's phase 0 decision."
                    }
                  ],
                  "routes": [
                    {
                      "operation": "charge",
                      "driver": "kulpay_balance",
                      "method": "kulpay_balance",
                      "mode": "live",
                      "scope": "on_net",
                      "enabled": true
                    },
                    {
                      "operation": "payout",
                      "driver": "kulpay_balance",
                      "method": "kulpay_balance",
                      "mode": "live",
                      "scope": "off_net",
                      "enabled": false
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/service-event-sinks": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listServiceEventSinks",
        "summary": "List the service event sinks",
        "description": "The platform's service event sinks (at most one per mode, IG-22). Operator only: a key with `admin:read` (never keyless); merchant keys get 403 whatever their scopes.",
        "responses": {
          "200": {
            "description": "The sinks; never a secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ServiceEventSink"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KgA1serviceEventSink000001",
                      "mode": "live",
                      "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events",
                      "event_types": [
                        "payment.*"
                      ],
                      "enabled": true,
                      "created_by": "key_3KfOperator00000000000001",
                      "created_at": "2026-10-12T08:00:00Z",
                      "updated_at": "2026-10-12T08:00:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:read`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:read"
                  },
                  "missing_scope": "admin:read"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "createServiceEventSink",
        "summary": "Create a mode's service event sink",
        "description": "For KulOffice (IG-22): one destination per mode receiving the payment events of every account in that mode, in the same v3 envelope merchants get (`aggregate_id` is the provider payment id; read the payment back with `GET /v1/service/payments/{id}`), signed `KulPay-Signature: t=<unix>,v1=<hex HMAC-SHA256 of \"<t>.<body>\">` with one platform secret, returned here once. Delivered by the webhook worker: at least once, 24-hour schedule, dead letter, replay; deduplicate on `KulPay-Event-Id`. Not a merchant webhook endpoint: no merchant can list, create or change it, and merchant endpoint secrets never sign it. Records `config.service_event_sink.created`. Operator only: a key with `admin:write` (never keyless); merchant keys get 403 whatever their scopes. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ],
                    "description": "Default: the key's mode."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "`payment.*` (default) or payment webhook event types; nothing else."
                  }
                }
              },
              "example": {
                "mode": "live",
                "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sink with its `secret`, shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceEventSink"
                },
                "example": {
                  "id": "3KgA1serviceEventSink000001",
                  "mode": "live",
                  "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events",
                  "event_types": [
                    "payment.*"
                  ],
                  "enabled": true,
                  "created_by": "key_3KfOperator00000000000001",
                  "created_at": "2026-10-12T08:00:00Z",
                  "updated_at": "2026-10-12T08:00:00Z",
                  "secret": "k9G2v7\u2026f1wQ"
                }
              }
            }
          },
          "400": {
            "description": "A bad URL, mode or event type (only payment events).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "url must be an absolute http(s) URL of at most 500 characters",
                  "error": {
                    "code": "invalid_request",
                    "message": "url must be an absolute http(s) URL of at most 500 characters"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:write`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:write"
                  },
                  "missing_scope": "admin:write"
                }
              }
            }
          },
          "409": {
            "description": "The mode already has a sink; `idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "service_event_sink_exists",
                  "detail": "this mode already has a service event sink: change it with PATCH",
                  "error": {
                    "code": "service_event_sink_exists",
                    "message": "this mode already has a service event sink: change it with PATCH"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/service-event-sinks/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "getServiceEventSink",
        "summary": "A service event sink",
        "description": "Operator only: a key with `admin:read` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The sink; never its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceEventSink"
                },
                "example": {
                  "id": "3KgA1serviceEventSink000001",
                  "mode": "live",
                  "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events",
                  "event_types": [
                    "payment.*"
                  ],
                  "enabled": true,
                  "created_by": "key_3KfOperator00000000000001",
                  "created_at": "2026-10-12T08:00:00Z",
                  "updated_at": "2026-10-12T08:00:00Z"
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:read`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:read"
                  },
                  "missing_scope": "admin:read"
                }
              }
            }
          },
          "404": {
            "description": "No such sink.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "service event sink not found",
                  "error": {
                    "code": "not_found",
                    "message": "service event sink not found"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Events"
        ],
        "operationId": "updateServiceEventSink",
        "summary": "Change a service event sink",
        "description": "Change its URL or subscription, or disable or enable it. A disabled sink is sent nothing: deliveries due meanwhile are dead-lettered; replay them once it is enabled. Records `config.service_event_sink.updated`. Operator only: a key with `admin:write` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "event_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "enabled": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The sink.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceEventSink"
                },
                "example": {
                  "id": "3KgA1serviceEventSink000001",
                  "mode": "live",
                  "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events",
                  "event_types": [
                    "payment.*"
                  ],
                  "enabled": false,
                  "created_by": "key_3KfOperator00000000000001",
                  "created_at": "2026-10-12T08:00:00Z",
                  "updated_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "A bad URL or event type.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "url must be an absolute http(s) URL of at most 500 characters",
                  "error": {
                    "code": "invalid_request",
                    "message": "url must be an absolute http(s) URL of at most 500 characters"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:write`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:write"
                  },
                  "missing_scope": "admin:write"
                }
              }
            }
          },
          "404": {
            "description": "No such sink.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "service event sink not found",
                  "error": {
                    "code": "not_found",
                    "message": "service event sink not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/service-event-sinks/{id}/roll-secret": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "rollServiceEventSinkSecret",
        "summary": "Roll the service event sink's secret",
        "description": "A new platform secret (shown once); the old one keeps signing until `previous_secret_expires_at` (default 24 h, 1 minute to 7 days), so every delivery meanwhile carries a `v1=` for both and the receiver deploys the new one without a gap (as PSP-8.8). Records `credential.service_event_sink_secret.rotated`. Operator only: a key with `admin:write` (never keyless); merchant keys get 403 whatever their scopes. An `Idempotency-Key` on this request binds the key to it as usual, but the success response holds a secret and is never kept (IG-30): the same key and request again is `409 idempotency_replay_unavailable` (never the secret a second time; list the resources to find the one it made), another request under the key `409 idempotency_key_reused`.",
        "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": "The sink with its new `secret`, shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceEventSink"
                },
                "example": {
                  "id": "3KgA1serviceEventSink000001",
                  "mode": "live",
                  "url": "https://api.kuloffice.co.mz/v1/integrations/kulpay-gateway/events",
                  "event_types": [
                    "payment.*"
                  ],
                  "enabled": true,
                  "created_by": "key_3KfOperator00000000000001",
                  "created_at": "2026-10-12T08:00:00Z",
                  "updated_at": "2026-10-12T08:00:00Z",
                  "secret": "Zq4m1\u202688aB",
                  "previous_secret_expires_at": "2026-10-13T08:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "A bad grace period.",
            "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"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:write`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:write"
                  },
                  "missing_scope": "admin:write"
                }
              }
            }
          },
          "404": {
            "description": "No such sink.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "service event sink not found",
                  "error": {
                    "code": "not_found",
                    "message": "service event sink not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/service-event-sinks/{id}/deliveries": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "listServiceEventSinkDeliveries",
        "summary": "The service event sink's deliveries",
        "description": "The last 30 days, newest first. Operator only: a key with `admin:read` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "exhausted"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 200,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deliveries.",
            "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",
                            "enum": [
                              "pending",
                              "delivered",
                              "exhausted"
                            ]
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "response_code": {
                            "type": "integer"
                          },
                          "duration_ms": {
                            "type": "integer"
                          },
                          "last_error": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "delivered_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "next_attempt_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KgA2deliverySink0000000001",
                      "event_id": "3KgA2eventPaymentSucceeded1",
                      "event_type": "payment.succeeded",
                      "status": "delivered",
                      "attempts": 1,
                      "response_code": 204,
                      "duration_ms": 41,
                      "created_at": "2026-10-12T08:05:00Z",
                      "delivered_at": "2026-10-12T08:05:00Z"
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "400": {
            "description": "A bad status.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "status is pending, delivered or exhausted",
                  "error": {
                    "code": "invalid_request",
                    "message": "status is pending, delivered or exhausted"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:read`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:read"
                  },
                  "missing_scope": "admin:read"
                }
              }
            }
          },
          "404": {
            "description": "No such sink.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "service event sink not found",
                  "error": {
                    "code": "not_found",
                    "message": "service event sink not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/service-event-sinks/{id}/replay": {
      "post": {
        "tags": [
          "Events"
        ],
        "operationId": "replayServiceEventSink",
        "summary": "Replay the service event sink's deliveries",
        "description": "Queues again, once per event and under the same event ids, the sink's deliveries from an event on (`since`, default the last 30 days), the dead-lettered ones (`status=exhausted`), or both. Records `service_event_sink.replayed`. Operator only: a key with `admin:write` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "An event id delivered to the sink."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "exhausted",
                "delivered"
              ]
            }
          }
        ],
        "responses": {
          "202": {
            "description": "How many were queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "queued": 3
                }
              }
            }
          },
          "400": {
            "description": "A bad status, or `since` names no delivery to this sink.",
            "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 exhausted or delivered",
                  "error": {
                    "code": "invalid_request",
                    "message": "status must be exhausted or delivered"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:write`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:write"
                  },
                  "missing_scope": "admin:write"
                }
              }
            }
          },
          "404": {
            "description": "No such sink.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "service event sink not found",
                  "error": {
                    "code": "not_found",
                    "message": "service event sink not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The sink is disabled.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "endpoint_disabled",
                  "detail": "the sink is disabled: enable it before replaying",
                  "error": {
                    "code": "endpoint_disabled",
                    "message": "the sink is disabled: enable it before replaying"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/application-projections": {
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "listApplicationProjections",
        "summary": "Application projections pushed to KulOffice",
        "description": "The projection pushes (IG-22), newest change first, and whether the push is configured (`enabled`; off unless `KULPAY_KULOFFICE_BASE_URL` and its service principal are set). Operator only: a key with `admin:read` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "failed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The pushes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApplicationProjectionPush"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "application_id": "3KfOz1mApplicationWeb00001",
                      "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                      "mode": "live",
                      "external_ref": "pacapp_7Hq2",
                      "state": "delivered",
                      "generation": 3,
                      "attempts": 1,
                      "next_attempt_at": "2026-10-12T08:00:00Z",
                      "last_status": 200,
                      "last_body": "{\"gateway_account_id\":\"3Ky2WEM0zMfI2pD5i2iBhSEQcB2\",\"gateway_application_id\":\"3KfOz1mApplicationWeb00001\",\"gateway_binding_id\":\"3KfOyBindingMpesa000000001\",\"binding_label\":\"M-Pesa till \u2022\u2022\u2022\u20222345\",\"binding_status\":\"active\"}",
                      "delivered_at": "2026-10-12T08:00:01Z",
                      "created_at": "2026-10-11T09:00:00Z",
                      "updated_at": "2026-10-12T08:00:01Z"
                    }
                  ],
                  "total": 1,
                  "enabled": true
                }
              }
            }
          },
          "400": {
            "description": "A bad state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "state is pending, delivered or failed",
                  "error": {
                    "code": "invalid_request",
                    "message": "state is pending, delivered or failed"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:read`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:read"
                  },
                  "missing_scope": "admin:read"
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/application-projections/{application_id}/push": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "pushApplicationProjection",
        "summary": "Push an application's projection again",
        "description": "Arms the push of a merchant application with an `external_ref` at once: an operator's retry of a failed push, or a resync. The job sends the current projection; an unchanged one KulOffice already has is not sent. Operator only: a key with `admin:write` (never keyless); merchant keys get 403 whatever their scopes.",
        "parameters": [
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "The push, pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApplicationProjectionPush"
                },
                "example": {
                  "application_id": "3KfOz1mApplicationWeb00001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "external_ref": "pacapp_7Hq2",
                  "state": "pending",
                  "generation": 4,
                  "attempts": 0,
                  "next_attempt_at": "2026-10-12T08:00:00Z",
                  "last_status": 200,
                  "last_body": "{\"gateway_account_id\":\"3Ky2WEM0zMfI2pD5i2iBhSEQcB2\",\"gateway_application_id\":\"3KfOz1mApplicationWeb00001\",\"gateway_binding_id\":\"3KfOyBindingMpesa000000001\",\"binding_label\":\"M-Pesa till \u2022\u2022\u2022\u20222345\",\"binding_status\":\"active\"}",
                  "delivered_at": "2026-10-12T08:00:01Z",
                  "created_at": "2026-10-11T09:00:00Z",
                  "updated_at": "2026-10-12T08:00:01Z"
                }
              }
            }
          },
          "400": {
            "description": "The application has no `external_ref`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "the application has no external_ref: KulOffice does not know it",
                  "error": {
                    "code": "invalid_request",
                    "message": "the application has no external_ref: KulOffice does not know it"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an operator key with `admin:write`, or no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: admin:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: admin:write"
                  },
                  "missing_scope": "admin:write"
                }
              }
            }
          },
          "404": {
            "description": "No such application.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "merchant application not found",
                  "error": {
                    "code": "not_found",
                    "message": "merchant application not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The push is not configured.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "projection_push_not_configured",
                  "detail": "the KulOffice projection push is not configured (KULPAY_KULOFFICE_BASE_URL)",
                  "error": {
                    "code": "projection_push_not_configured",
                    "message": "the KulOffice projection push is not configured (KULPAY_KULOFFICE_BASE_URL)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/payables": {
      "get": {
        "tags": [
          "Charges"
        ],
        "operationId": "getServicePayable",
        "summary": "A payment's payable and deductions, read by a trusted service",
        "description": "For KulOffice (IG-22): the payable of any account's managed pass-through payment, by the Gateway's payment id, in the key's own mode: gross, `fee_instruction_status` (`awaiting` or `accepted`) and `fee_instruction_id`, the deduction lines (`platform_fee` per deducted assessment with its `instruction_id` and `assessment_id`, `provider_cost`, `reserve`, then each `adjustment`), each with its settlement arrangement version, the net, the state, and the batch and `settled_at` once settled. Requires `service:settlements:read`, an operator-issued scope (a service account, or a key issued with `admin:write`); an account cannot grant it to itself. A payment of the other mode, an unknown one or one without a payable (not managed pass-through) is not found.",
        "parameters": [
          {
            "name": "provider_payment_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicePayable"
                },
                "example": {
                  "payable_id": "3KgB1payableManaged00000001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                  "payment_id": "ord_1001",
                  "funds_flow": "managed",
                  "currency": "MZN",
                  "arrangement_id": "3KgB0arrangementManaged0001",
                  "arrangement_version": 2,
                  "gross_minor": 12345,
                  "fee_instruction_status": "accepted",
                  "fee_instruction_id": "kfi_01J9ZK3M7Q",
                  "deductions": [
                    {
                      "kind": "platform_fee",
                      "amount_minor": 864,
                      "arrangement_id": "3KgB0arrangementManaged0001",
                      "arrangement_version": 2,
                      "instruction_id": "kfi_01J9ZK3M7Q",
                      "assessment_id": "kfa_01J9ZK3M7R"
                    },
                    {
                      "kind": "provider_cost",
                      "amount_minor": 123,
                      "arrangement_id": "3KgB0arrangementManaged0001",
                      "arrangement_version": 2
                    },
                    {
                      "kind": "reserve",
                      "amount_minor": 617,
                      "arrangement_id": "3KgB0arrangementManaged0001",
                      "arrangement_version": 2
                    }
                  ],
                  "net_minor": 10741,
                  "state": "settled",
                  "available_at": "2026-10-12T00:00:00Z",
                  "batch_id": "3KgB2batchManaged0000000001",
                  "settled_at": "2026-10-12T16:20:00.5Z",
                  "reserve_release_at": "2026-10-18T09:30:05Z",
                  "created_at": "2026-10-11T09:30:05.123456789Z",
                  "updated_at": "2026-10-12T16:20:00.5Z"
                }
              }
            }
          },
          "400": {
            "description": "No `provider_payment_id`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "provider_payment_id is required",
                  "error": {
                    "code": "invalid_request",
                    "message": "provider_payment_id is required"
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:settlements:read` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:settlements:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:settlements:read"
                  },
                  "missing_scope": "service:settlements:read"
                }
              }
            }
          },
          "404": {
            "description": "No payable for this payment in the key's mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no payable for this payment",
                  "error": {
                    "code": "not_found",
                    "message": "no payable for this payment"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/settlements/{batch_id}": {
      "get": {
        "tags": [
          "Charges"
        ],
        "operationId": "getServiceSettlement",
        "summary": "A settlement batch and its payments, read by a trusted service",
        "description": "For KulOffice (IG-80): a settlement batch of any account, by its id, in the key's own mode, to record a collecting settlement (the platform service event sink delivers `settlement.batch.created` and `settlement.batch.settled`, aggregate `settlement_batch`, with the batch id). Amounts are integers in minor units: `gross_minor`, `fees_minor` (the fee taken, positive; a fee given back in the batch lowers it) and `net_minor` (the batch's signed net, refunds, adjustments and carried amounts included, so not always gross less fees). `status` is the batch status (`created`, `ready`, `paying`, `paid`, `settled`, `failed`, `carried_forward`, `recorded`); `external_reference` is the operators' evidence (`\"\"` until settled) and `settled_at` (RFC 3339) is absent until settled. `lines` has one entry per payment with movements in the batch: its gross, its fee (as `fees_minor`), the KulOffice `fee_instruction_id` accepted for it (`\"\"` for none) and the net of all its movements in the batch. Movements that are not a payment's (a carried net, a returned payout, a merchant payout) count in the totals only. A live collecting payment is batched only once its fee instruction is accepted. Requires `service:settlements:read`, an operator-issued scope (a service account, or a key issued with `admin:write`); an account cannot grant it to itself. A batch of the other mode, or an unknown id, is not found.",
        "parameters": [
          {
            "name": "batch_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The batch.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceSettlement"
                },
                "example": {
                  "batch_id": "3KgC1batchCollecting00000001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "funds_flow": "collecting",
                  "currency": "MZN",
                  "status": "settled",
                  "gross_minor": 100000,
                  "fees_minor": 700,
                  "net_minor": 99300,
                  "external_reference": "BCI-TRF-0080",
                  "settled_at": "2026-10-13T16:20:00.5Z",
                  "lines": [
                    {
                      "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                      "gross_minor": 100000,
                      "fee_minor": 700,
                      "fee_instruction_id": "kfi_01J9ZK3M7Q",
                      "net_minor": 99300
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:settlements:read` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:settlements:read",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:settlements:read"
                  },
                  "missing_scope": "service:settlements:read"
                }
              }
            }
          },
          "404": {
            "description": "Unknown batch id, or a batch of the other mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "settlement batch not found",
                  "error": {
                    "code": "not_found",
                    "message": "settlement batch not found"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/payments/{provider_payment_id}/fee-instruction": {
      "put": {
        "tags": [
          "Charges"
        ],
        "operationId": "putServiceFeeInstruction",
        "summary": "Send a payment's fee instruction (trusted service)",
        "description": "For KulOffice (IG-29, owner decision 2026-10-12): the fee KulOffice captured for a managed pass-through payment of any account, in the key's own mode. One complete instruction per payment, ever, bound to the payment's account, mode and currency. Every line is stored as evidence; the merchant-borne `settlement_deduction` lines become the payable's `platform_fee` deduction (`provider_split` and other lines are evidence only), and the payable can then be promoted to available: until then it stays provisional and is never batched. An empty `lines` array is the explicit zero fee. A payable reversed before the instruction keeps it for the record only (`effect: record_only`). The same `instruction_id` with an identical body returns 200 with the stored instruction; any difference is 409 `fee_instruction_conflict`. Since IG-80 a succeeded collecting payment is accepted too: the fee-schedule fee posted at success is given back and the merchant-borne `settlement_deduction` lines are posted as its fee in the collecting balance (no `payable_id`, `effect: deducted`); a live collecting payment is not batched until its instruction is accepted. A direct or internal payment, a managed one without a payable yet, or a collecting one that has not succeeded: 409 `fee_instruction_not_applicable`. Requires `service:fees:write`, an operator-issued scope. Live mode only gates promotion: in test mode (which KulOffice ignores) payables are promoted with a zero platform fee, and an instruction arriving after its payable was batched is kept for the record (`effect: record_only`).",
        "parameters": [
          {
            "name": "provider_payment_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Gateway's payment id."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional; when sent it must equal `instruction_id`. The route is idempotent by `instruction_id` itself, not by the generic Idempotency-Key store, so a changed body is answered by its own conflict code."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeeInstructionRequest"
              },
              "example": {
                "instruction_id": "kfi_01J9ZK3M7Q",
                "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                "transaction_id": "txn_01J9ZK2A1B",
                "pricing_capture_id": "pcap_01J9ZK2A1C",
                "plan_version_id": "plv_01J9Y0AA01",
                "currency": "MZN",
                "complete": true,
                "lines": [
                  {
                    "assessment_id": "kfa_01J9ZK3M7R",
                    "component_code": "platform_fee",
                    "kind": "platform_fee",
                    "bearer": "merchant",
                    "collection_method": "settlement_deduction",
                    "amount_minor": 864,
                    "recognized_at": "2026-10-12T09:30:06Z"
                  },
                  {
                    "assessment_id": "kfa_01J9ZK3M7S",
                    "component_code": "mpesa_cost",
                    "kind": "provider_cost",
                    "bearer": "merchant",
                    "collection_method": "provider_split",
                    "amount_minor": 123,
                    "recognized_at": "2026-10-12T09:30:06Z"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted, or an identical replay: the stored instruction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeInstruction"
                },
                "example": {
                  "instruction_id": "kfi_01J9ZK3M7Q",
                  "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                  "transaction_id": "txn_01J9ZK2A1B",
                  "pricing_capture_id": "pcap_01J9ZK2A1C",
                  "plan_version_id": "plv_01J9Y0AA01",
                  "currency": "MZN",
                  "complete": true,
                  "lines": [
                    {
                      "assessment_id": "kfa_01J9ZK3M7R",
                      "component_code": "platform_fee",
                      "kind": "platform_fee",
                      "bearer": "merchant",
                      "collection_method": "settlement_deduction",
                      "amount_minor": 864,
                      "recognized_at": "2026-10-12T09:30:06Z"
                    },
                    {
                      "assessment_id": "kfa_01J9ZK3M7S",
                      "component_code": "mpesa_cost",
                      "kind": "provider_cost",
                      "bearer": "merchant",
                      "collection_method": "provider_split",
                      "amount_minor": 123,
                      "recognized_at": "2026-10-12T09:30:06Z"
                    }
                  ],
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "payable_id": "3KgB1payableManaged00000001",
                  "deduction_minor": 864,
                  "effect": "deducted",
                  "accepted_at": "2026-10-12T09:31:00Z"
                }
              }
            }
          },
          "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": "The key lacks `service:fees:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:fees:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:fees:write"
                  },
                  "missing_scope": "service:fees:write"
                }
              }
            }
          },
          "404": {
            "description": "Unknown payment, or a payment of the other mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "payment not found",
                  "error": {
                    "code": "not_found",
                    "message": "payment not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`fee_instruction_conflict`: the payment already has another instruction, or this one changed, or its id belongs to another payment. `fee_instruction_not_applicable`: a direct or internal payment, a managed one without a payable, or a collecting one that has not succeeded.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "examples": {
                  "conflict": {
                    "value": {
                      "type": "about:blank",
                      "title": "Conflict",
                      "status": 409,
                      "code": "fee_instruction_conflict",
                      "detail": "the payment already has fee instruction kfi_01J9ZK3M7Q: one instruction per payment, ever; send a fee adjustment",
                      "error": {
                        "code": "fee_instruction_conflict",
                        "message": "the payment already has fee instruction kfi_01J9ZK3M7Q: one instruction per payment, ever; send a fee adjustment"
                      }
                    }
                  },
                  "not_applicable": {
                    "value": {
                      "type": "about:blank",
                      "title": "Conflict",
                      "status": 409,
                      "code": "fee_instruction_not_applicable",
                      "detail": "the payment is not managed pass-through (funds flow \"direct\"): the Gateway holds no payable to deduct from, so the fee is collected by its other method",
                      "error": {
                        "code": "fee_instruction_not_applicable",
                        "message": "the payment is not managed pass-through (funds flow \"direct\"): the Gateway holds no payable to deduct from, so the fee is collected by its other method"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`fee_instruction_invalid`: partial, malformed, another currency, a negative amount, or a deduction beyond the gross less provider cost and reserve. Also an Idempotency-Key other than the instruction_id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Entity",
                  "status": 422,
                  "code": "fee_instruction_invalid",
                  "detail": "the settlement deduction 13000 exceeds the gross less provider cost and reserve (11605)",
                  "error": {
                    "code": "fee_instruction_invalid",
                    "message": "the settlement deduction 13000 exceeds the gross less provider cost and reserve (11605)"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/payments/{provider_payment_id}/fee-adjustments": {
      "post": {
        "tags": [
          "Charges"
        ],
        "operationId": "postServiceFeeAdjustment",
        "summary": "Adjust an assessment of a payment's fee instruction (trusted service)",
        "description": "For KulOffice (IG-29): a signed change of an assessment in the payment's accepted fee instruction, idempotent by `adjustment_id`. While the payable is provisional or available (or disputed) a merchant settlement-deduction adjustment changes its `platform_fee` deduction; once reserved for settlement, settled or reversed it is carried as a `fee_adjustment` line on the merchant's next batched payable. The payable read shows each as an `adjustment` line (`adjustment_kind` `fee_adjustment`) with `instruction_id`, `assessment_id` and KulOffice's id as `fee_adjustment_id`. `instruction_id`, `currency`, `cause` and `recognized_at` are accepted and stored as evidence. Requires `service:fees:write`, an operator-issued scope. For a collecting payment (IG-80) the fee change is posted at once: `platform_fee` while no batch holds the payment (it settles in the payment's batch), `carried` once batched (it lands in the next collecting batch); the payment's fee stays between 0 and its gross.",
        "parameters": [
          {
            "name": "provider_payment_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The Gateway's payment id."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Optional; when sent it must equal `adjustment_id`. The route is idempotent by `adjustment_id` itself, not by the generic Idempotency-Key store, so a changed body is answered by its own conflict code."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeeAdjustmentRequest"
              },
              "example": {
                "adjustment_id": "kfa_01J9ZM0001",
                "instruction_id": "kfi_01J9ZK3M7Q",
                "assessment_id": "kfa_01J9ZK3M7R",
                "amount_minor": -100,
                "currency": "MZN",
                "cause": "pricing_correction",
                "recognized_at": "2026-10-12T10:59:00Z"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeAdjustment"
                },
                "example": {
                  "adjustment_id": "kfa_01J9ZM0001",
                  "instruction_id": "kfi_01J9ZK3M7Q",
                  "assessment_id": "kfa_01J9ZK3M7R",
                  "amount_minor": -100,
                  "currency": "MZN",
                  "cause": "pricing_correction",
                  "recognized_at": "2026-10-12T10:59:00Z",
                  "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                  "id": "3KgB3feeAdjustment000000001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "payable_id": "3KgB1payableManaged00000001",
                  "effect": "platform_fee",
                  "accepted_at": "2026-10-12T11:00:00Z"
                }
              }
            }
          },
          "200": {
            "description": "An identical replay: the stored adjustment (with `target_payable_id` once a carried one was applied).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeeAdjustment"
                },
                "example": {
                  "adjustment_id": "kfa_01J9ZM0001",
                  "instruction_id": "kfi_01J9ZK3M7Q",
                  "assessment_id": "kfa_01J9ZK3M7R",
                  "amount_minor": -100,
                  "currency": "MZN",
                  "cause": "pricing_correction",
                  "recognized_at": "2026-10-12T10:59:00Z",
                  "provider_payment_id": "3KfP0q7Lr2servicePayment01",
                  "id": "3KgB3feeAdjustment000000001",
                  "account_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                  "mode": "live",
                  "payable_id": "3KgB1payableManaged00000001",
                  "effect": "platform_fee",
                  "accepted_at": "2026-10-12T11:00:00Z"
                }
              }
            }
          },
          "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": "The key lacks `service:fees:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:fees:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:fees:write"
                  },
                  "missing_scope": "service:fees:write"
                }
              }
            }
          },
          "404": {
            "description": "Unknown payment, or a payment of the other mode.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "payment not found",
                  "error": {
                    "code": "not_found",
                    "message": "payment not found"
                  }
                }
              }
            }
          },
          "409": {
            "description": "`fee_adjustment_conflict`: the adjustment_id was accepted with a different body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "fee_adjustment_conflict",
                  "detail": "adjustment kfadj_01J9ZM0001 was accepted with a different body",
                  "error": {
                    "code": "fee_adjustment_conflict",
                    "message": "adjustment kfadj_01J9ZM0001 was accepted with a different body"
                  }
                }
              }
            }
          },
          "422": {
            "description": "`fee_adjustment_invalid`: no accepted instruction yet, an unknown assessment, an amount of 0, or a result below zero or beyond the gross less provider cost and reserve. Also an Idempotency-Key other than the adjustment_id, or an instruction_id or currency that is not the payment's.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Unprocessable Entity",
                  "status": 422,
                  "code": "fee_adjustment_invalid",
                  "detail": "assessment kfa_missing is not in the payment's fee instruction kfi_01J9ZK3M7Q",
                  "error": {
                    "code": "fee_adjustment_invalid",
                    "message": "assessment kfa_missing is not in the payment's fee instruction kfi_01J9ZK3M7Q"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/payments/{id}/approve": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "approveManualPayment",
        "summary": "Confirm a manual payment",
        "description": "An operator confirms that the money of a manual payment arrived (IG-52). Only a payment taken by the manual-confirmation connector (driver `manual`) can be confirmed; in test mode also one on the simulated fleet. Any other payment, or one with no method yet, is 409 `not_manual_payment`: its outcome is its provider's to report. Test mode settles the payment at once (200). Live mode settles nothing: it answers 202 with a maker-checker request (kind `payment.manual_confirm`), and the payment succeeds, with its ledger charge and its collection evidence, when a different person approves the request (`POST /v1/admin/approvals/{id}/decide`, `accounts:write`); a repeat while it is pending returns the same request. The optional `reason` (or `X-Change-Reason`) is the request's. Requires `payments:confirm`, a scope only an operator issues (no merchant key set includes it); a trusted service's key is refused (403 `service_key_not_allowed`). `POST /v1/payments/{id}/confirm` and the console's manual queue behave the same.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "reason": "Deposit slip 4471 matches the BCI statement of 12 October"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Test mode: the payment, succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "failure_code": {
                      "type": "string"
                    },
                    "failure_message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "id": "3KWPog5AqNigZC2DABGSG1qEabV",
                  "status": "succeeded",
                  "amount": "100",
                  "currency": "MZN",
                  "method": "manual"
                }
              }
            }
          },
          "202": {
            "description": "Live mode: waiting for a second person. `{status: pending_approval, approval, payment}`; the payment is unchanged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending_approval"
                      ]
                    },
                    "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"
                        }
                      }
                    },
                    "payment": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "string"
                        },
                        "currency": {
                          "type": "string"
                        },
                        "method": {
                          "type": "string"
                        },
                        "failure_code": {
                          "type": "string"
                        },
                        "failure_message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "example": {
                  "status": "pending_approval",
                  "approval": {
                    "id": "3KbIf49pShcqGqJUCjC2svkIwHr",
                    "kind": "payment.manual_confirm",
                    "subject_id": "3KWPog5AqNigZC2DABGSG1qEabV",
                    "tenant_id": "3Ky2WEM0zMfI2pD5i2iBhSEQcB2",
                    "payload": "{\"binding_id\":\"3KWPoS1bR2IZiR2ksqOoozIDdc2\",\"method\":\"manual\",\"amount\":\"100\",\"currency\":\"MZN\"}",
                    "reason": "Deposit slip 4471 matches the BCI statement of 12 October",
                    "prepared_by": "operator:ana",
                    "status": "pending",
                    "created_at": "2026-10-12T10:00:00Z"
                  },
                  "payment": {
                    "id": "3KWPog5AqNigZC2DABGSG1qEabV",
                    "status": "requires_action",
                    "amount": "100",
                    "currency": "MZN",
                    "method": "manual"
                  }
                }
              }
            }
          },
          "403": {
            "description": "`insufficient_scope` (no `payments:confirm`) or `service_key_not_allowed` (a trusted service's key).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "service_key_not_allowed",
                  "detail": "a trusted service's key (it holds service:payments:read) cannot confirm a payment; an operator's key confirms",
                  "error": {
                    "code": "service_key_not_allowed",
                    "message": "a trusted service's key (it holds service:payments:read) cannot confirm a payment; an operator's key confirms"
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`not_manual_payment`: not taken by the manual-confirmation connector; or `conflict`: no longer awaiting a decision.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not a manual payment",
                  "status": 409,
                  "code": "not_manual_payment",
                  "detail": "payments: only a payment taken by the manual-confirmation connector can be confirmed",
                  "error": {
                    "code": "not_manual_payment",
                    "message": "payments: only a payment taken by the manual-confirmation connector can be confirmed"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/custody/accounts": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listCustodyAccounts",
        "summary": "Custody accounts",
        "description": "The external accounts behind the ledger's custody pools (IG-71). `mode` narrows to live or test. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustodyAccount"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KcuSt0dyAcc0untBCI00000001",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "client_funds",
                      "statement_account": "000800001234567890157",
                      "label": "BCI safeguarding",
                      "created_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T09:00:00Z"
                    },
                    {
                      "id": "3KcuSt0dyAcc0untMPESA000002",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "provider_custody",
                      "connector_binding_id": "3KmPesaB1nd1ng00000000000001",
                      "label": "M-Pesa platform account",
                      "created_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T09:05:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "addCustodyAccount",
        "summary": "Register a custody account",
        "description": "Puts an external account behind a ledger custody pool: `mode` (default live), `currency`, `ledger_code` (`client_funds` or `provider_custody`), and its balance source: `connector_binding_id` (a binding in the same mode whose connector offers a balance read) and/or `statement_account` (the account number on imported statements; live only). With neither, the account is reported unverifiable. Records `config.custody_account.added`. Requires `accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "currency",
                  "ledger_code"
                ],
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "currency": {
                    "type": "string"
                  },
                  "ledger_code": {
                    "type": "string",
                    "enum": [
                      "client_funds",
                      "provider_custody"
                    ]
                  },
                  "connector_binding_id": {
                    "type": "string"
                  },
                  "statement_account": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "mode": "live",
                "currency": "MZN",
                "ledger_code": "client_funds",
                "statement_account": "000800001234567890157",
                "label": "BCI safeguarding"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustodyAccount"
                },
                "example": {
                  "id": "3KcuSt0dyAcc0untBCI00000001",
                  "mode": "live",
                  "currency": "MZN",
                  "ledger_code": "client_funds",
                  "statement_account": "000800001234567890157",
                  "label": "BCI safeguarding",
                  "created_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                  "created_at": "2026-10-12T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Statements are live only: a test-mode custody account cannot name a statement account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "statements are live only: a test-mode custody account cannot name a statement account",
                  "error": {
                    "code": "invalid_request",
                    "message": "statements are live only: a test-mode custody account cannot name a statement account"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/custody/reconciliations/run": {
      "post": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "runCustodyReconciliation",
        "summary": "Run the custody reconciliation",
        "description": "Compares every custody pool of `mode` (both when omitted) with its external balances: each account's connector balance read, else its latest imported statement's closing balance. The pool's ledger balance is taken as of the readings' cutoff (the statement cycle's cutoff, or the profile's cutoff on the last booking date, or the provider's as-of). A pool with ledger postings and no account, an account with neither source, a failed read or readings at different cutoffs is `unverifiable`. A `difference` opens a `custody_difference` reconciliation exception (it cannot be written off). Records `custody.reconciliation.recorded` per new result. A pool already reconciled at the same cutoff returns the recorded result and opens nothing. Never posts a journal or moves money. Requires `reconciliation:write`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The results, new or already recorded at their cutoff.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustodyReconciliation"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KcuSt0dyRec0nc1l1at10n0001",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "client_funds",
                      "cutoff": "2026-10-11T21:00:00Z",
                      "status": "matched",
                      "ledger_minor": 1250031250,
                      "external_minor": 1250031250,
                      "difference_minor": 0,
                      "readings": "[{\"custody_account_id\":\"3KcuSt0dyAcc0untBCI00000001\",\"label\":\"BCI safeguarding\",\"statement_account\":\"000800001234567890157\",\"source\":\"statement\",\"balance_minor\":1250031250,\"as_of\":\"2026-10-11T21:00:00Z\",\"statement_file_id\":\"3KdhOrsZV8VzDwRsx2nNjQAi6kX\",\"parse_version\":1}]",
                      "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T07:00:03Z"
                    },
                    {
                      "id": "3KcuSt0dyRec0nc1l1at10n0002",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "provider_custody",
                      "cutoff": "2026-10-12T06:59:58Z",
                      "status": "difference",
                      "ledger_minor": 48210000,
                      "external_minor": 48200000,
                      "difference_minor": -10000,
                      "readings": "[{\"custody_account_id\":\"3KcuSt0dyAcc0untMPESA000002\",\"label\":\"M-Pesa platform account\",\"connector_binding_id\":\"3KmPesaB1nd1ng00000000000001\",\"source\":\"provider_balance\",\"balance_minor\":48200000,\"as_of\":\"2026-10-12T06:59:58Z\",\"provider_reference\":\"BAL-20261012-0700\"}]",
                      "exception_id": "3KexCept10nCust0dy000000001",
                      "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T07:00:03Z"
                    }
                  ],
                  "total": 2
                }
              }
            }
          },
          "400": {
            "description": "Mode must be live or test.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Bad Request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "mode must be live or test",
                  "error": {
                    "code": "invalid_request",
                    "message": "mode must be live or test"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/custody/reconciliations": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "listCustodyReconciliations",
        "summary": "Custody reconciliation results",
        "description": "Recorded results, newest first, narrowed by `mode`, `currency`, `ledger_code` and `status`; `limit` up to 500 (default 100). Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "test"
              ]
            }
          },
          {
            "name": "currency",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ledger_code",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "client_funds",
                "provider_custody"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "matched",
                "difference",
                "unverifiable"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "`{data, total}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CustodyReconciliation"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "3KcuSt0dyRec0nc1l1at10n0002",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "provider_custody",
                      "cutoff": "2026-10-12T06:59:58Z",
                      "status": "difference",
                      "ledger_minor": 48210000,
                      "external_minor": 48200000,
                      "difference_minor": -10000,
                      "readings": "[{\"custody_account_id\":\"3KcuSt0dyAcc0untMPESA000002\",\"label\":\"M-Pesa platform account\",\"connector_binding_id\":\"3KmPesaB1nd1ng00000000000001\",\"source\":\"provider_balance\",\"balance_minor\":48200000,\"as_of\":\"2026-10-12T06:59:58Z\",\"provider_reference\":\"BAL-20261012-0700\"}]",
                      "exception_id": "3KexCept10nCust0dy000000001",
                      "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T07:00:03Z"
                    },
                    {
                      "id": "3KcuSt0dyRec0nc1l1at10n0001",
                      "mode": "live",
                      "currency": "MZN",
                      "ledger_code": "client_funds",
                      "cutoff": "2026-10-11T21:00:00Z",
                      "status": "matched",
                      "ledger_minor": 1250031250,
                      "external_minor": 1250031250,
                      "difference_minor": 0,
                      "readings": "[{\"custody_account_id\":\"3KcuSt0dyAcc0untBCI00000001\",\"label\":\"BCI safeguarding\",\"statement_account\":\"000800001234567890157\",\"source\":\"statement\",\"balance_minor\":1250031250,\"as_of\":\"2026-10-11T21:00:00Z\",\"statement_file_id\":\"3KdhOrsZV8VzDwRsx2nNjQAi6kX\",\"parse_version\":1}]",
                      "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T07:00:03Z"
                    },
                    {
                      "id": "3KcuSt0dyRec0nc1l1at10n0003",
                      "mode": "test",
                      "currency": "MZN",
                      "ledger_code": "provider_custody",
                      "cutoff": "2026-10-12T07:00:03Z",
                      "status": "unverifiable",
                      "ledger_minor": 500000,
                      "external_minor": 0,
                      "difference_minor": 0,
                      "readings": "[]",
                      "reason": "no custody account is registered for this ledger pool",
                      "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                      "created_at": "2026-10-12T07:00:03Z"
                    }
                  ],
                  "total": 3
                }
              }
            }
          }
        }
      }
    },
    "/v1/admin/custody/reconciliations/{id}": {
      "get": {
        "tags": [
          "Reconciliation"
        ],
        "operationId": "getCustodyReconciliation",
        "summary": "A custody reconciliation result",
        "description": "One result with its evidence. Requires `reconciliation:read`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustodyReconciliation"
                },
                "example": {
                  "id": "3KcuSt0dyRec0nc1l1at10n0002",
                  "mode": "live",
                  "currency": "MZN",
                  "ledger_code": "provider_custody",
                  "cutoff": "2026-10-12T06:59:58Z",
                  "status": "difference",
                  "ledger_minor": 48210000,
                  "external_minor": 48200000,
                  "difference_minor": -10000,
                  "readings": "[{\"custody_account_id\":\"3KcuSt0dyAcc0untMPESA000002\",\"label\":\"M-Pesa platform account\",\"connector_binding_id\":\"3KmPesaB1nd1ng00000000000001\",\"source\":\"provider_balance\",\"balance_minor\":48200000,\"as_of\":\"2026-10-12T06:59:58Z\",\"provider_reference\":\"BAL-20261012-0700\"}]",
                  "exception_id": "3KexCept10nCust0dy000000001",
                  "run_by": "operator:7f3c9a2e-5b1d-4e8a-9c6f-2d4b8e1a0c37",
                  "created_at": "2026-10-12T07:00:03Z"
                }
              }
            }
          },
          "404": {
            "description": "No such custody reconciliation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Not Found",
                  "status": 404,
                  "code": "not_found",
                  "detail": "no such custody reconciliation",
                  "error": {
                    "code": "not_found",
                    "message": "no such custody reconciliation"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/partners/lsp/{external_ref}": {
      "put": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "provisionLSP",
        "summary": "Provision a biller KulOffice approved, as an LSP in technical setup (trusted service)",
        "description": "KO-1 (owner decision 2026-10-20): KulOffice and KulPay are the same regulated entity, so KulPay relies on KulOffice's KYB and four-eyes approval of a biller and does not repeat it. One idempotent call per biller, by KulOffice's id for it. The first call, with `status: approved` and the `kyb` and `approval` decisions, creates the organisation (account active), its LSP straight in `technical_setup` with its KYB recorded as KulOffice's (`psp.kyb.approved` v2, `basis: kuloffice`), its sandbox, and the biller (origin `kuloffice`, technical status `sandbox`, status `draft`); it makes the technical contact the portal's first admin and emails them a single-use sign-in link valid 72 hours. It answers 201 with `created: true`. Later calls answer 200: they keep the commercial fields equal to KulOffice's (name, category, NUIT, logo, contacts, credit accounts; the default bank account is the remittance account) and its commercial status (`approved`, `suspended`, `terminated`); a new technical contact is invited, and each address in `revoked_contacts` (KO-2: a technical contact replaced in KulOffice with a reason and consent) loses its portal access at once: user removed, sessions ended, told by email; a revoked address is never invited back by a sync. A replacement the business makes in the portal is sent to KulOffice as the webhook `biller.technical_contact_replaced` (phone masked). Those fields are read only at KulPay (`409 owned_by_origin` on the admin routes). KulPay owns the technical side: sandbox, certification, go-live with four eyes. Every technical status is the webhook `biller.integration_status` on the key's account (KulOffice's), in both modes. The biller takes payments only when KulOffice has it approved and its LSP is live. Without the portal's address the invitation cannot go out: nothing is created, `503 portal_not_configured` with Retry-After. Requires `service:partners:write` (platform, operator-only). Never certifies or puts an LSP live.",
        "parameters": [
          {
            "name": "external_ref",
            "in": "path",
            "required": true,
            "description": "KulOffice's id for the biller (letters, digits and _ . : -, at most 64).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "legal_name",
                  "nuit",
                  "category",
                  "status",
                  "contacts"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "The biller's code at KulPay: 2 to 20 capitals, digits or _. It does not change (`409 code_immutable`)."
                  },
                  "legal_name": {
                    "type": "string"
                  },
                  "trading_name": {
                    "type": "string",
                    "description": "Shown to payers; the legal name when absent."
                  },
                  "nuit": {
                    "type": "string"
                  },
                  "category": {
                    "enum": [
                      "electricity",
                      "water",
                      "telecom",
                      "tv",
                      "internet",
                      "education",
                      "insurance",
                      "government",
                      "other"
                    ]
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO-3166 alpha-2; MZ when absent."
                  },
                  "logo_url": {
                    "type": "string",
                    "description": "https only."
                  },
                  "status": {
                    "enum": [
                      "approved",
                      "suspended",
                      "terminated"
                    ],
                    "description": "KulOffice's commercial status. Only `approved` creates."
                  },
                  "contacts": {
                    "type": "array",
                    "description": "By role; a technical contact with an email is required (KulPay invites them).",
                    "items": {
                      "type": "object",
                      "required": [
                        "role",
                        "email"
                      ],
                      "properties": {
                        "role": {
                          "enum": [
                            "technical",
                            "commercial",
                            "finance",
                            "compliance"
                          ]
                        },
                        "name": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        }
                      }
                    }
                  },
                  "credit_accounts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "type",
                        "number"
                      ],
                      "properties": {
                        "credit_account_id": {
                          "type": "string"
                        },
                        "type": {
                          "enum": [
                            "bank_account",
                            "mobile_wallet",
                            "kulpay_balance"
                          ]
                        },
                        "institution": {
                          "type": "string"
                        },
                        "number": {
                          "type": "string"
                        },
                        "holder": {
                          "type": "string"
                        },
                        "default": {
                          "type": "boolean"
                        }
                      }
                    }
                  },
                  "kyb": {
                    "type": "object",
                    "required": [
                      "by",
                      "at"
                    ],
                    "properties": {
                      "by": {
                        "type": "string",
                        "description": "KulOffice's operator who decided."
                      },
                      "at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "ref": {
                        "type": "string",
                        "description": "KulOffice's reference for the decision, kept for the audit trail."
                      }
                    }
                  },
                  "approval": {
                    "type": "object",
                    "required": [
                      "by",
                      "at"
                    ],
                    "properties": {
                      "by": {
                        "type": "string",
                        "description": "KulOffice's operator who decided."
                      },
                      "at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "ref": {
                        "type": "string",
                        "description": "KulOffice's reference for the decision, kept for the audit trail."
                      }
                    }
                  },
                  "reason": {
                    "type": "string",
                    "description": "Why the commercial status changed."
                  },
                  "revoked_contacts": {
                    "type": "array",
                    "description": "KO-2: people whose access KulOffice revoked when it replaced them as the technical contact. The Gateway removes their portal user, ends their sessions and tells them; a revoked address is never invited back by a later call, only by an admin's own invitation in the portal.",
                    "items": {
                      "type": "object",
                      "required": [
                        "email"
                      ],
                      "properties": {
                        "email": {
                          "type": "string"
                        },
                        "reason": {
                          "type": "string"
                        },
                        "by": {
                          "type": "string",
                          "description": "Who in KulOffice revoked it."
                        },
                        "at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "code": "AGUAS_UMB",
                "legal_name": "\u00c1guas do Umbeluzi, SA",
                "trading_name": "\u00c1guas do Umbeluzi",
                "nuit": "400123456",
                "category": "water",
                "country": "MZ",
                "status": "approved",
                "contacts": [
                  {
                    "role": "technical",
                    "name": "Ana T\u00e9cnica",
                    "email": "ana.tecnica@umbeluzi.co.mz"
                  },
                  {
                    "role": "commercial",
                    "name": "Rui Comercial",
                    "email": "rui@umbeluzi.co.mz",
                    "phone": "+258841110000"
                  }
                ],
                "credit_accounts": [
                  {
                    "credit_account_id": "bca_1",
                    "type": "bank_account",
                    "institution": "BCI",
                    "number": "000123456789",
                    "holder": "\u00c1guas do Umbeluzi",
                    "default": true
                  }
                ],
                "kyb": {
                  "by": "op_maker",
                  "at": "2026-10-20T08:00:00Z",
                  "ref": "kyb_77"
                },
                "approval": {
                  "by": "op_checker",
                  "at": "2026-10-20T09:00:00Z",
                  "ref": "apr_91"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already provisioned: its commercial fields updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "partner": {
                      "type": "object",
                      "description": "The gateway's side of the biller: its LSP's lifecycle, its KYB (basis kuloffice), the biller's statuses, the sandbox and the technical contact's invitation (email masked)."
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "created": false,
                  "partner": {
                    "external_ref": "bil_3KQ7",
                    "origin": "kuloffice",
                    "psp_id": "3KxWinedj8leqZYlcZUkoxDbGEZ",
                    "account_id": "3KxWijN5vpLi9ArGysmtkgU8KY0",
                    "lifecycle_status": "technical_setup",
                    "kyb": {
                      "status": "approved",
                      "basis": "kuloffice",
                      "approved_by": "op_checker",
                      "approved_at": "2026-10-20T09:00:00Z",
                      "decision_ref": "apr_91"
                    },
                    "biller": {
                      "id": "3KxWiihSQFA9KqUw9XlXiOhMucA",
                      "code": "AGUAS_UMB",
                      "status": "draft",
                      "commercial_status": "suspended",
                      "technical_status": "sandbox"
                    },
                    "sandbox": {
                      "enabled": true,
                      "enabled_at": "2026-10-20T12:37:09.0079515Z"
                    },
                    "invitation": {
                      "email": "a\u2022\u2022\u2022@umbeluzi.co.mz",
                      "status": "sent"
                    },
                    "created_at": "2026-10-20T12:37:08.9787124Z",
                    "updated_at": "2026-10-20T14:37:09.0407043+02:00"
                  }
                }
              }
            }
          },
          "201": {
            "description": "Provisioned: the LSP in technical setup, its sandbox opened, its technical contact invited.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "partner": {
                      "type": "object",
                      "description": "The gateway's side of the biller: its LSP's lifecycle, its KYB (basis kuloffice), the biller's statuses, the sandbox and the technical contact's invitation (email masked)."
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "created": true,
                  "partner": {
                    "external_ref": "bil_3KQ7",
                    "origin": "kuloffice",
                    "psp_id": "3KxWinedj8leqZYlcZUkoxDbGEZ",
                    "account_id": "3KxWijN5vpLi9ArGysmtkgU8KY0",
                    "lifecycle_status": "technical_setup",
                    "kyb": {
                      "status": "approved",
                      "basis": "kuloffice",
                      "approved_by": "op_checker",
                      "approved_at": "2026-10-20T09:00:00Z",
                      "decision_ref": "apr_91"
                    },
                    "biller": {
                      "id": "3KxWiihSQFA9KqUw9XlXiOhMucA",
                      "code": "AGUAS_UMB",
                      "status": "draft",
                      "commercial_status": "approved",
                      "technical_status": "sandbox"
                    },
                    "sandbox": {
                      "enabled": true,
                      "enabled_at": "2026-10-20T12:37:09.0079515Z"
                    },
                    "invitation": {
                      "email": "a\u2022\u2022\u2022@umbeluzi.co.mz",
                      "status": "sent"
                    },
                    "created_at": "2026-10-20T12:37:08.9787124Z",
                    "updated_at": "2026-10-20T12:37:08.9787124Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "A field is missing or invalid; `detail` names it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "invalid_request",
                  "detail": "code: 2 to 20 capital letters, digits or _",
                  "error": {
                    "code": "invalid_request",
                    "message": "code: 2 to 20 capital letters, digits or _"
                  },
                  "status": 400,
                  "title": "Bad Request",
                  "type": "about:blank"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The key does not hold `service:partners:write`.", "content": {"application/problem+json": {"schema": {"$ref": "#/components/schemas/Problem"}, "example": {"type":"about:blank","title":"Forbidden","status":403,"code":"insufficient_scope","detail":"missing required scope: service:partners:write","error":{"code":"insufficient_scope","message":"missing required scope: service:partners:write"},"missing_scope":"service:partners:write"}}}
          },
          "409": {
            "description": "`not_approved`: nothing is provisioned before KulOffice approves; `code_taken`: another biller has the code; `code_immutable`: the code changed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "not_approved",
                  "detail": "KulPay provisions a biller once KulOffice approved it: send status approved, with its kyb and approval decisions.",
                  "error": {
                    "code": "not_approved",
                    "message": "KulPay provisions a biller once KulOffice approved it: send status approved, with its kyb and approval decisions."
                  },
                  "status": 409,
                  "title": "Not approved",
                  "type": "about:blank"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "description": "`portal_not_configured`: the invitation cannot go out; nothing was created; retry after Retry-After.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "portal_not_configured",
                  "detail": "The partner portal's address is not configured at the gateway, so the technical contact cannot be invited. Nothing was created; retry once an operator sets it.",
                  "error": {
                    "code": "portal_not_configured",
                    "message": "The partner portal's address is not configured at the gateway, so the technical contact cannot be invited. Nothing was created; retry once an operator sets it."
                  },
                  "status": 503,
                  "title": "Portal not configured",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "getProvisionedLSP",
        "summary": "Read a provisioned LSP (trusted service)",
        "description": "The gateway's side of the biller KulOffice provisioned under its id: lifecycle, KYB basis, statuses, sandbox and invitation. `404` when this key's account provisioned none under it. Requires `service:partners:write`.",
        "parameters": [
          {
            "name": "external_ref",
            "in": "path",
            "required": true,
            "description": "KulOffice's id for the biller (letters, digits and _ . : -, at most 64).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The provisioned LSP.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "partner": {
                      "type": "object",
                      "description": "The gateway's side of the biller: its LSP's lifecycle, its KYB (basis kuloffice), the biller's statuses, the sandbox and the technical contact's invitation (email masked)."
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "partner": {
                    "external_ref": "bil_3KQ7",
                    "origin": "kuloffice",
                    "psp_id": "3KxWinedj8leqZYlcZUkoxDbGEZ",
                    "account_id": "3KxWijN5vpLi9ArGysmtkgU8KY0",
                    "lifecycle_status": "technical_setup",
                    "kyb": {
                      "status": "approved",
                      "basis": "kuloffice",
                      "approved_by": "op_checker",
                      "approved_at": "2026-10-20T09:00:00Z",
                      "decision_ref": "apr_91"
                    },
                    "biller": {
                      "id": "3KxWiihSQFA9KqUw9XlXiOhMucA",
                      "code": "AGUAS_UMB",
                      "status": "draft",
                      "commercial_status": "approved",
                      "technical_status": "sandbox"
                    },
                    "sandbox": {
                      "enabled": true,
                      "enabled_at": "2026-10-20T12:37:09.0079515Z"
                    },
                    "invitation": {
                      "email": "a\u2022\u2022\u2022@umbeluzi.co.mz",
                      "status": "sent"
                    },
                    "created_at": "2026-10-20T12:37:08.9787124Z",
                    "updated_at": "2026-10-20T14:37:09.0223855+02:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No LSP provisioned under it by this account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "code": "not_found",
                  "detail": "No LSP was provisioned under this external_ref by this key's account.",
                  "error": {
                    "code": "not_found",
                    "message": "No LSP was provisioned under this external_ref by this key's account."
                  },
                  "status": 404,
                  "title": "Not found",
                  "type": "about:blank"
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/partners/lsp/{external_ref}/invitation": {
      "post": {
        "tags": [
          "PSP onboarding"
        ],
        "operationId": "resendLSPInvitation",
        "summary": "Send the technical contact a new invitation (trusted service)",
        "description": "A new single-use sign-in link, valid 72 hours, to the technical contact; at most 5 links an hour to one address (`429`). The portal of a suspended or offboarded LSP is closed (`400`). Requires `service:partners:write`.",
        "parameters": [
          {
            "name": "external_ref",
            "in": "path",
            "required": true,
            "description": "KulOffice's id for the biller (letters, digits and _ . : -, at most 64).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "partner": {
                      "type": "object",
                      "description": "The gateway's side of the biller: its LSP's lifecycle, its KYB (basis kuloffice), the biller's statuses, the sandbox and the technical contact's invitation (email masked)."
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "partner": {
                    "external_ref": "bil_3KQ7",
                    "origin": "kuloffice",
                    "psp_id": "3KxWinedj8leqZYlcZUkoxDbGEZ",
                    "account_id": "3KxWijN5vpLi9ArGysmtkgU8KY0",
                    "lifecycle_status": "technical_setup",
                    "kyb": {
                      "status": "approved",
                      "basis": "kuloffice",
                      "approved_by": "op_checker",
                      "approved_at": "2026-10-20T09:00:00Z",
                      "decision_ref": "apr_91"
                    },
                    "biller": {
                      "id": "3KxWiihSQFA9KqUw9XlXiOhMucA",
                      "code": "AGUAS_UMB",
                      "status": "draft",
                      "commercial_status": "suspended",
                      "technical_status": "sandbox"
                    },
                    "sandbox": {
                      "enabled": true,
                      "enabled_at": "2026-10-20T12:37:09.0079515Z"
                    },
                    "invitation": {
                      "email": "a\u2022\u2022\u2022@umbeluzi.co.mz",
                      "status": "sent"
                    },
                    "created_at": "2026-10-20T12:37:08.9787124Z",
                    "updated_at": "2026-10-20T14:37:09.0407043+02:00"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/service/accounts": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "ensureServiceAccount",
        "summary": "Create or find a merchant tenant by external_ref (trusted service)",
        "description": "IG-76 (owner decision 2026-10-12, \"narrow service scopes\"): a trusted service (KulOffice) acts for a merchant with `account_id` and an explicit `mode`, holding the operator-issued `service:accounts:write` (platform, operator-only: `keys:manage` cannot grant it, a merchant key never holds it). The scope reaches another account only; on the caller's own account the merchant scopes apply. A service key never acts above its own mode: a test-mode key provisions test mode only (`403 mode_not_allowed`); a live-mode key may name test or live. Creates the merchant tenant the service names by its own id (`external_ref`, KulOffice's business account id) and answers 201 with `created: true`, or returns the tenant that already carries it with 200 and `created: false` (a retry, or two copies racing, end on the same account; a different `business_name` or `country` does not change a found tenant). A new tenant is born in status `test`; activating it asserts KYC and stays an operator's act. A create is refused while a configured key has no `account_id` (`409 ambiguous_keys`, IG-55). Requires `service:accounts:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "external_ref",
                  "business_name",
                  "country"
                ],
                "properties": {
                  "external_ref": {
                    "type": "string",
                    "description": "Letters, digits and _ . : - (at most 64)."
                  },
                  "business_name": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO-3166 alpha-2."
                  }
                }
              },
              "example": {
                "external_ref": "kba_01JABCDXYZ",
                "business_name": "Loja Acme Lda",
                "country": "MZ"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "object": {
                          "enum": [
                            "account"
                          ]
                        },
                        "external_ref": {
                          "type": "string",
                          "description": "The service's own id for the tenant (KulOffice's business account id); unique."
                        },
                        "business_name": {
                          "type": "string"
                        },
                        "country": {
                          "type": "string",
                          "description": "ISO-3166 alpha-2."
                        },
                        "status": {
                          "enum": [
                            "test",
                            "kyc_pending",
                            "active",
                            "suspended"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "account": {
                    "id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                    "object": "account",
                    "external_ref": "kba_01JABCDXYZ",
                    "business_name": "Loja Acme Lda",
                    "country": "MZ",
                    "status": "test",
                    "created_at": "2026-10-12T09:30:00Z",
                    "updated_at": "2026-10-12T09:30:00Z"
                  },
                  "created": true
                }
              }
            }
          },
          "200": {
            "description": "Found: the tenant that already carries `external_ref`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "object": {
                          "enum": [
                            "account"
                          ]
                        },
                        "external_ref": {
                          "type": "string",
                          "description": "The service's own id for the tenant (KulOffice's business account id); unique."
                        },
                        "business_name": {
                          "type": "string"
                        },
                        "country": {
                          "type": "string",
                          "description": "ISO-3166 alpha-2."
                        },
                        "status": {
                          "enum": [
                            "test",
                            "kyc_pending",
                            "active",
                            "suspended"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                },
                "example": {
                  "account": {
                    "id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                    "object": "account",
                    "external_ref": "kba_01JABCDXYZ",
                    "business_name": "Loja Acme Lda",
                    "country": "MZ",
                    "status": "test",
                    "created_at": "2026-10-12T09:30:00Z",
                    "updated_at": "2026-10-12T09:30:00Z"
                  },
                  "created": false
                }
              }
            }
          },
          "400": {
            "description": "A missing or malformed `external_ref`, `business_name` or `country`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "external_ref is required: letters, digits and _ . : - (at most 64)",
                  "error": {
                    "code": "invalid_request",
                    "message": "external_ref is required: letters, digits and _ . : - (at most 64)"
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:accounts:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:accounts:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:accounts:write"
                  },
                  "missing_scope": "service:accounts:write"
                }
              }
            }
          },
          "409": {
            "description": "`ambiguous_keys`: a configured key has no `account_id`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Existing keys are not bound to an account",
                  "status": 409,
                  "code": "ambiguous_keys",
                  "detail": "Key(s) legacy have no account_id, so they act on this deployment's default account. Set account_id on every key in the `keys:` registry first, then retry.",
                  "error": {
                    "code": "ambiguous_keys",
                    "message": "Key(s) legacy have no account_id, so they act on this deployment's default account. Set account_id on every key in the `keys:` registry first, then retry."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "findServiceAccount",
        "summary": "Find a merchant tenant by external_ref (trusted service)",
        "description": "IG-76: the tenant a trusted service provisioned under `external_ref`. A read under `service:accounts:write`.",
        "parameters": [
          {
            "name": "external_ref",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "object": {
                      "enum": [
                        "account"
                      ]
                    },
                    "external_ref": {
                      "type": "string",
                      "description": "The service's own id for the tenant (KulOffice's business account id); unique."
                    },
                    "business_name": {
                      "type": "string"
                    },
                    "country": {
                      "type": "string",
                      "description": "ISO-3166 alpha-2."
                    },
                    "status": {
                      "enum": [
                        "test",
                        "kyc_pending",
                        "active",
                        "suspended"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "object": "account",
                  "external_ref": "kba_01JABCDXYZ",
                  "business_name": "Loja Acme Lda",
                  "country": "MZ",
                  "status": "test",
                  "created_at": "2026-10-12T09:30:00Z",
                  "updated_at": "2026-10-12T09:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "No usable `external_ref`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Invalid request",
                  "status": 400,
                  "code": "invalid_request",
                  "detail": "external_ref is required: letters, digits and _ . : - (at most 64)",
                  "error": {
                    "code": "invalid_request",
                    "message": "external_ref is required: letters, digits and _ . : - (at most 64)"
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:accounts:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:accounts:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:accounts:write"
                  },
                  "missing_scope": "service:accounts:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: no tenant carries this `external_ref`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account carries this external_ref.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account carries this external_ref."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/api-keys": {
      "post": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "issueServiceKey",
        "summary": "Issue a merchant's application-bound API key (trusted service)",
        "description": "IG-76 (owner decision 2026-10-12, \"narrow service scopes\"): a trusted service (KulOffice) acts for a merchant with `account_id` and an explicit `mode`, holding the operator-issued `service:keys:write` (platform, operator-only: `keys:manage` cannot grant it, a merchant key never holds it). The scope reaches another account only; on the caller's own account the merchant scopes apply. A service key never acts above its own mode: a test-mode key provisions test mode only (`403 mode_not_allowed`); a live-mode key may name test or live. Issues an API key of `account_id` in `mode`, bound to `application_id`, which must be an active application of that account in that mode (another account's, another mode's or an unknown one: `404 application_not_found`). `type` `sk` (the merchant set), `rk` (its `scopes`) or `pk`; merchant scopes only: a platform or operator-only scope is `403 insufficient_scope`. A live key without an expiry gets 12 months. The secret is returned once (`Cache-Control: no-store`); the same Idempotency-Key and request again is `409 idempotency_replay_unavailable`, never the secret a second time. Records `credential.api_key.created`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "account_id",
                  "mode",
                  "application_id"
                ],
                "properties": {
                  "account_id": {
                    "type": "string"
                  },
                  "mode": {
                    "enum": [
                      "live",
                      "test"
                    ]
                  },
                  "application_id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "enum": [
                      "sk",
                      "rk",
                      "pk"
                    ]
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "no_expiry": {
                    "type": "boolean"
                  },
                  "allowed_ips": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "max_amount": {
                    "type": "string"
                  },
                  "daily_amount": {
                    "type": "string"
                  },
                  "currency": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                "mode": "live",
                "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3",
                "name": "Loja online",
                "type": "rk",
                "scopes": [
                  "payments:read",
                  "payments:write"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Issued; `secret` is shown this once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_id": {
                      "type": "string"
                    },
                    "id": {
                      "type": "string"
                    },
                    "mode": {
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "last4": {
                      "type": "string"
                    },
                    "type": {
                      "enum": [
                        "sk",
                        "rk",
                        "pk"
                      ]
                    },
                    "legacy": {
                      "type": "boolean"
                    },
                    "secret": {
                      "type": "string",
                      "description": "The key, on issue only."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "last_used_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "revoked_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "use_count": {
                      "type": "integer"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "status": {
                      "enum": [
                        "active",
                        "expiring",
                        "expired",
                        "revoked"
                      ]
                    },
                    "allowed_ips": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "max_amount": {
                      "type": "string"
                    },
                    "daily_amount": {
                      "type": "string"
                    },
                    "limit_currency": {
                      "type": "string"
                    },
                    "application_id": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                  "id": "3KUXR1keyProvisioned0000001",
                  "mode": "live",
                  "name": "Loja online",
                  "scopes": [
                    "payments:read",
                    "payments:write"
                  ],
                  "last4": "9fQ2",
                  "type": "rk",
                  "legacy": false,
                  "secret": "rk_live_EXAMPLEonlyNOTaREALkey000000000000000000",
                  "created_at": "2026-10-12T09:31:00Z",
                  "use_count": 0,
                  "expires_at": "2027-10-12T09:31:00Z",
                  "status": "expiring",
                  "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
                }
              }
            }
          },
          "400": {
            "description": "`mode_required`: a trusted service's call names no `mode`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Mode required",
                  "status": 400,
                  "code": "mode_required",
                  "detail": "A trusted service names the mode it provisions in: send mode test or live.",
                  "error": {
                    "code": "mode_required",
                    "message": "A trusted service names the mode it provisions in: send mode test or live."
                  }
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:keys:write` (a merchant key, whatever its scopes, or no key); a platform or operator-only scope asked for (`insufficient_scope`); or `mode_not_allowed`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:keys:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:keys:write"
                  },
                  "missing_scope": "service:keys:write"
                }
              }
            }
          },
          "404": {
            "description": "`application_not_found`: the application is not the account's in this mode (another account's is not found).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Application not found",
                  "status": 404,
                  "code": "application_not_found",
                  "detail": "No application with this id belongs to the account in this mode.",
                  "error": {
                    "code": "application_not_found",
                    "message": "No application with this id belongs to the account in this mode."
                  }
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_replay_unavailable`: a replay of a request that succeeded; `idempotency_key_reused`: another request under the key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Conflict",
                  "status": 409,
                  "code": "idempotency_replay_unavailable",
                  "detail": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost.",
                  "error": {
                    "code": "idempotency_replay_unavailable",
                    "message": "A request with this Idempotency-Key already succeeded. Its response held a secret that is shown once and not kept, so it cannot be replayed: list the resources to find the one it created, and revoke it if its secret was lost."
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "listServiceKeys",
        "summary": "List a merchant's application-bound API keys (trusted service)",
        "description": "IG-76: the account's keys that belong to an application (a key in no application is not listed), never a secret. Requires `service:keys:write`. A test-mode key lists test-mode keys only.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "live",
                "test"
              ]
            },
            "description": "One mode; omitted: both (a test-mode key: test)."
          },
          {
            "name": "application_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The keys.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "account_id": {
                            "type": "string"
                          },
                          "id": {
                            "type": "string"
                          },
                          "mode": {
                            "enum": [
                              "live",
                              "test"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "last4": {
                            "type": "string"
                          },
                          "type": {
                            "enum": [
                              "sk",
                              "rk",
                              "pk"
                            ]
                          },
                          "legacy": {
                            "type": "boolean"
                          },
                          "secret": {
                            "type": "string",
                            "description": "The key, on issue only."
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "revoked_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "use_count": {
                            "type": "integer"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "enum": [
                              "active",
                              "expiring",
                              "expired",
                              "revoked"
                            ]
                          },
                          "allowed_ips": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "max_amount": {
                            "type": "string"
                          },
                          "daily_amount": {
                            "type": "string"
                          },
                          "limit_currency": {
                            "type": "string"
                          },
                          "application_id": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "account_id": "3KUXPz8rTq1Lm2Nb4Vc6Xs7Za9e",
                      "id": "3KUXR1keyProvisioned0000001",
                      "mode": "live",
                      "name": "Loja online",
                      "scopes": [
                        "payments:read",
                        "payments:write"
                      ],
                      "last4": "9fQ2",
                      "type": "rk",
                      "legacy": false,
                      "created_at": "2026-10-12T09:31:00Z",
                      "use_count": 0,
                      "expires_at": "2027-10-12T09:31:00Z",
                      "status": "expiring",
                      "application_id": "3KUXQ43bztiuf9tCMNgbcrYvne3"
                    }
                  ],
                  "total": 1
                }
              }
            }
          },
          "403": {
            "description": "The key lacks `service:keys:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:keys:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:keys:write"
                  },
                  "missing_scope": "service:keys:write"
                }
              }
            }
          },
          "404": {
            "description": "`account_not_found`: `account_id` names no account.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Account not found",
                  "status": 404,
                  "code": "account_not_found",
                  "detail": "No account with id 3KmissingAccount000000000001 exists on this deployment.",
                  "error": {
                    "code": "account_not_found",
                    "message": "No account with id 3KmissingAccount000000000001 exists on this deployment."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service/api-keys/{id}": {
      "delete": {
        "tags": [
          "Payment acceptance"
        ],
        "operationId": "revokeServiceKey",
        "summary": "Revoke a merchant's application-bound API key (trusted service)",
        "description": "IG-76: revokes an application-bound key of `account_id`. A key in no application, another account's, a revoked one, or (for a test-mode caller) a live one is `404 not_found`. Records `credential.api_key.revoked`. Requires `service:keys:write`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "403": {
            "description": "The key lacks `service:keys:write` (a merchant key, whatever its scopes), or there is no key.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "about:blank",
                  "title": "Forbidden",
                  "status": 403,
                  "code": "insufficient_scope",
                  "detail": "missing required scope: service:keys:write",
                  "error": {
                    "code": "insufficient_scope",
                    "message": "missing required scope: service:keys:write"
                  },
                  "missing_scope": "service:keys:write"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "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"
          },
          "settlement": {
            "type": "string",
            "enum": [
              "none"
            ],
            "description": "`none` when the connector the payment was submitted through does not move real money (a card rail on an acquirer's test key); omitted otherwise. Decided by the payment's binding, never by the platform default for the method."
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "The payment's mode, from the key that created it."
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The amount in ISO 4217 minor units (MZN 250.50 is 25050)."
          },
          "connector_binding_id": {
            "type": "string",
            "description": "The stable id of the connector binding the payment was submitted through (the merchant's own, or a routed PSP's). Fixed once anything reached a provider, even if the binding is edited, replaced or removed later. Absent for a deployment-default connector. `connector_name` is only a label."
          },
          "connector_ref": {
            "type": "string",
            "description": "The provider's reference for the payment."
          },
          "settled_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the gateway recorded the provider's confirmed success. Present only on a live payment whose own binding moves real money; set once. A caller that credits money on a payment requires it, together with the mode, amount, currency and binding it expects."
          },
          "settled_amount": {
            "type": "string",
            "description": "The settled amount, a decimal at the currency's exponent. Present with `settled_at`."
          },
          "settled_amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The settled amount in ISO 4217 minor units. Present with `settled_at`."
          }
        }
      },
      "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"
          },
          "settlement": {
            "type": "string",
            "enum": [
              "none"
            ],
            "description": "`none` when the connector the payment was submitted through does not move real money (a card rail on an acquirer's test key); omitted otherwise. Decided by the payment's binding, never by the platform default for the method."
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "The payment's mode, from the key that created it."
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The amount in ISO 4217 minor units (MZN 250.50 is 25050)."
          },
          "connector_binding_id": {
            "type": "string",
            "description": "The stable id of the connector binding the payment was submitted through (the merchant's own, or a routed PSP's). Fixed once anything reached a provider, even if the binding is edited, replaced or removed later. Absent for a deployment-default connector. `connector_name` is only a label."
          },
          "connector_ref": {
            "type": "string",
            "description": "The provider's reference for the payment."
          },
          "settled_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the gateway recorded the provider's confirmed success. Present only on a live payment whose own binding moves real money; set once. A caller that credits money on a payment requires it, together with the mode, amount, currency and binding it expects."
          },
          "settled_amount": {
            "type": "string",
            "description": "The settled amount, a decimal at the currency's exponent. Present with `settled_at`."
          },
          "settled_amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The settled amount in ISO 4217 minor units. Present with `settled_at`."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "What the caller attached to the charge, so it can check the payment is the one it asked for."
          }
        }
      },
      "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_inactive",
          "application_incomplete",
          "application_not_allowed",
          "audit_unavailable",
          "audit_unsigned",
          "biller_unavailable",
          "callback_rejected",
          "capability_not_enabled",
          "certification_running",
          "client_certificate_required",
          "config_required",
          "connector_session_expired",
          "connector_session_not_found",
          "connector_session_used",
          "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_replay_unavailable",
          "idempotency_unavailable",
          "insufficient_balance",
          "payout_destination_not_approved",
          "insufficient_scope",
          "internal_error",
          "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",
          "license_required",
          "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_final",
          "not_found",
          "not_ready",
          "not_receiptable",
          "not_refundable",
          "not_replayable",
          "not_reversible",
          "not_submittable",
          "not_submitted",
          "not_supported",
          "onboarding_unavailable",
          "option_locked",
          "partner_reference_conflict",
          "pilot_limit",
          "quote_expired",
          "quote_invalidated",
          "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",
          "nothing_to_settle",
          "fee_instruction_invalid",
          "fee_instruction_conflict",
          "fee_instruction_not_applicable",
          "fee_adjustment_invalid",
          "fee_adjustment_conflict",
          "movement_scope_disabled",
          "route_not_enabled",
          "service_event_sink_exists",
          "projection_push_not_configured",
          "not_manual_payment",
          "service_key_not_allowed",
          "mode_required",
          "mode_not_allowed",
          "application_not_found",
          "not_approved",
          "code_taken",
          "code_immutable",
          "portal_not_configured",
          "owned_by_origin"
        ]
      },
      "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"
                }
              }
            }
          },
          "legacy_code": {
            "type": "string",
            "description": "The code this condition had before ERR-2, when it changed. Sent for one release; branch on `code`.",
            "deprecated": true
          },
          "error": {
            "type": "object",
            "deprecated": true,
            "description": "The old error envelope ({code, message}, with the old code), sent for one release so clients that read error.code keep working.",
            "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": {
          "kind": {
            "type": "string",
            "description": "psp (a payment provider) or lsp (a biller).",
            "enum": [
              "psp",
              "lsp"
            ]
          },
          "partner_type": {
            "type": "string",
            "description": "The partner type, a code of the partner_types catalogue (GET /v1/catalogs/partner_types?kind=psp). It sets the template the technical setup starts from."
          },
          "biller_category": {
            "type": "string",
            "description": "For an LSP: its sector, a code of the biller_sectors catalogue."
          },
          "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": {
          "kind": {
            "type": "string",
            "description": "psp or lsp.",
            "enum": [
              "psp",
              "lsp"
            ]
          },
          "partner_type": {
            "type": "string",
            "description": "The partner type chosen at application."
          },
          "blocked_capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Capabilities closed by a past-due requirement set to block (see GET /v1/psps/{id}/requirements)."
          },
          "contract_file_id": {
            "type": "string",
            "description": "The signed contract copy, stored encrypted."
          },
          "next_review_at": {
            "type": "string",
            "description": "When the periodic review falls due, by risk tier (high 6, medium 12, low 24 months).",
            "format": "date-time"
          },
          "last_reminder_at": {
            "type": "string",
            "description": "When the partner was last reminded by email of its unfinished technical setup (by an operator, or by the system after 3 days without progress).",
            "format": "date-time"
          },
          "logo_status": {
            "type": "string",
            "description": "The logo review: pending, approved or rejected."
          },
          "logo_file_id": {
            "type": "string"
          },
          "logo_pending_file_id": {
            "type": "string"
          },
          "logo_note": {
            "type": "string"
          },
          "logo_updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "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,
            "description": "The fees, in the shape of a fee schedule rule. A method may have several lines: one per transaction type, or one per amount band. The first line that covers a payment applies; bands of the same type, method and currency may not overlap. Settlement reconciliation works out the expected fees with them.",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string",
                  "description": "A transaction type (SALE, PAYOUT, CASH_IN\u2026); empty or * for any."
                },
                "method": {
                  "type": "string",
                  "description": "A method, or * for any."
                },
                "min_amount": {
                  "type": "string",
                  "description": "The band's lower bound, inclusive."
                },
                "max_amount": {
                  "type": "string",
                  "description": "The band's upper bound, exclusive; empty for none."
                },
                "min_fee": {
                  "type": "string"
                },
                "max_fee": {
                  "type": "string"
                },
                "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": {
          "live_key_id": {
            "type": "string",
            "description": "The live key issued at pilot."
          },
          "live_key_revealable": {
            "type": "boolean",
            "description": "The live key is still to be shown, once, in the portal."
          },
          "keys": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "The partner account's API keys (legacy ones to rotate)."
          },
          "oauth_clients": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "client_certificates": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "require_mtls": {
            "type": "boolean"
          },
          "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"
          }
        }
      },
      "CatalogItem": {
        "type": "object",
        "description": "One item of a back-office catalogue. The code is stable: it is what the APIs and stored choices use.",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "By language (pt, en)."
          },
          "help": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "retired": {
            "type": "boolean",
            "description": "No longer offered; who chose it keeps it."
          },
          "order": {
            "type": "integer"
          },
          "applies_to": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "psp",
                "lsp"
              ]
            },
            "description": "The partner kinds offered it; empty is every partner."
          },
          "required_for": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "psp",
                "lsp"
              ]
            },
            "description": "The partner kinds that must have it."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "url": {
            "type": "string",
            "description": "Where the item is consulted (an official list), https only."
          },
          "template": {
            "type": "object",
            "description": "A partner type's template: the technical setup documents it fills, by step (capability_matrix, orientation_profile, statement_profile, webhooks)."
          },
          "type": {
            "type": "string",
            "description": "Requirements catalogue: what it asks for (document, field, configuration, test, decision, acceptance)."
          },
          "stage": {
            "type": "string",
            "description": "Requirements catalogue: the stage it becomes due in and must be met to leave (kyb, commercial, technical, certification, pilot, live)."
          },
          "due_days": {
            "type": "integer",
            "description": "Requirements catalogue: days after it becomes due until it is past due; 0 never."
          },
          "who": {
            "type": "string",
            "description": "Requirements catalogue: who does it (partner, maker, checker, system)."
          },
          "blocks": {
            "type": "string",
            "description": "Requirements catalogue: the capability that only opens with it (sandbox, pilot, live_payments, payouts, remittances)."
          },
          "on_past_due": {
            "type": "string",
            "description": "Requirements catalogue: warn (default) or block, whether a past-due requirement closes the capability it protects."
          }
        }
      },
      "Catalog": {
        "type": "object",
        "description": "A back-office catalogue in force: the lists the forms and the APIs offer (partner types, roles, transaction types, countries, currencies, sectors, KYB documents, webhook events, statement formats, reasons, requirements, screening lists). Versioned; a new version is active once a second person approves it, and a regulatory catalogue needs compliance approval.",
        "properties": {
          "code": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "regulatory": {
            "type": "boolean"
          },
          "bound": {
            "type": "boolean",
            "description": "Its codes are bound to the gateway (items can be renamed or retired, not added)."
          },
          "version": {
            "type": "integer",
            "description": "0 is the built-in version."
          },
          "activated_at": {
            "type": "string",
            "format": "date-time"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CatalogItem"
            }
          }
        }
      },
      "CatalogVersion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "catalog": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "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"
          }
        }
      },
      "PartnerRequirement": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "help": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "document, field, configuration, test, decision or acceptance."
          },
          "required": {
            "type": "boolean"
          },
          "stage": {
            "type": "string"
          },
          "who": {
            "type": "string",
            "enum": [
              "partner",
              "maker",
              "checker",
              "system"
            ]
          },
          "blocks": {
            "type": "string",
            "description": "The capability it protects."
          },
          "on_past_due": {
            "type": "string",
            "enum": [
              "warn",
              "block"
            ]
          },
          "status": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          },
          "due_since": {
            "type": "string",
            "format": "date-time"
          },
          "due_by": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PartnerRequirements": {
        "type": "object",
        "description": "A partner's requirements by state, each list in catalogue order, and the capabilities closed by a past-due requirement set to block.",
        "properties": {
          "psp_id": {
            "type": "string"
          },
          "stage": {
            "type": "string"
          },
          "past_due": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "currently_due": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "eventually_due": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "pending_verification": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "done": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "optional": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartnerRequirement"
            }
          },
          "blocked": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Gate": {
        "type": "object",
        "properties": {
          "detail_pt": {
            "type": "string",
            "description": "The detail in Portuguese, as the operator pages show it."
          },
          "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",
              "TRANSFER",
              "NAME_LOOKUP",
              "STATUS_QUERY",
              "DISPUTE",
              "MANDATE",
              "RECURRING",
              "TOKENIZE",
              "BALANCE_INQUIRY",
              "SETTLEMENT",
              "FX_QUOTE"
            ]
          },
          "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",
                    "TRANSFER",
                    "NAME_LOOKUP",
                    "STATUS_QUERY",
                    "DISPUTE",
                    "MANDATE",
                    "RECURRING",
                    "TOKENIZE",
                    "BALANCE_INQUIRY",
                    "SETTLEMENT",
                    "FX_QUOTE"
                  ]
                }
              },
              "states": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "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"
          }
        }
      },
      "IncomingTransfer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "psp_id": {
            "type": "string",
            "description": "The reporting partner."
          },
          "partner_reference": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "received",
              "matched",
              "unmatched",
              "confirmed_final",
              "credited_notified",
              "returned",
              "compliance_review"
            ],
            "description": "`compliance_review` appears only in the beneficiary's view: the reporting partner is shown a held transfer as `confirmed_final` (matched and final), `matched` (matched) or `unmatched` (no beneficiary), and `creditable` follows the status shown. Operators (`GET /v1/admin/incoming-transfers`) see the real state."
          },
          "amount_minor": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "destination": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "deposit_reference",
                  "alias",
                  "virtual_account",
                  "payment_reference"
                ]
              },
              "value": {
                "type": "string"
              }
            }
          },
          "value_date": {
            "type": "string",
            "format": "date"
          },
          "final": {
            "type": "boolean"
          },
          "final_at": {
            "type": "string",
            "format": "date-time"
          },
          "creditable": {
            "type": "boolean",
            "description": "True only when the transfer is matched and confirmed final (`confirmed_final`)."
          },
          "beneficiary_name": {
            "type": "string",
            "description": "The holder's name, masked."
          },
          "beneficiary_account_id": {
            "type": "string",
            "description": "Only for the beneficiary."
          },
          "credit_reference": {
            "type": "string",
            "description": "Only for the beneficiary."
          },
          "credited_at": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "integer",
            "description": "Increases with every change; order events by it."
          },
          "received_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SettlementArrangement": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "superseded",
              "rejected"
            ]
          },
          "custody": {
            "type": "string",
            "enum": [
              "managed_pass_through",
              "merchant_direct"
            ]
          },
          "destination_ref": {
            "type": "string",
            "description": "The approved destination's identifier, masked (last four characters)."
          },
          "schedule": {
            "type": "string",
            "enum": [
              "daily",
              "weekly",
              "manual"
            ]
          },
          "schedule_weekday": {
            "type": "integer"
          },
          "available_after_days": {
            "type": "integer"
          },
          "minimum_settlement_minor": {
            "type": "integer"
          },
          "reserve_bps": {
            "type": "integer"
          },
          "reserve_hold_days": {
            "type": "integer"
          },
          "platform_fee_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 0,
            "description": "No longer used (IG-29, owner decision 2026-10-12): the platform fee is the one KulOffice captured, sent as each payment's fee instruction. A new version must leave it 0."
          },
          "platform_fee_fixed_minor": {
            "type": "integer",
            "minimum": 0,
            "maximum": 0,
            "description": "No longer used (IG-29, owner decision 2026-10-12): the platform fee is the one KulOffice captured, sent as each payment's fee instruction. A new version must leave it 0."
          },
          "provider_cost_bps": {
            "type": "integer"
          },
          "provider_cost_fixed_minor": {
            "type": "integer"
          },
          "refund_fee_policy": {
            "type": "string",
            "enum": [
              "retain",
              "proportional"
            ]
          },
          "refund_offset_policy": {
            "type": "string",
            "enum": [
              "offset_future_settlements",
              "invoice"
            ]
          },
          "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"
          }
        }
      },
      "MerchantPayable": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "payment_id": {
            "type": "string"
          },
          "arrangement_id": {
            "type": "string"
          },
          "arrangement_version": {
            "type": "integer"
          },
          "gross_minor": {
            "type": "integer"
          },
          "platform_fee_minor": {
            "type": "integer",
            "description": "The platform-fee deduction: what the payment's accepted fee instruction (its merchant settlement_deduction lines) and fee adjustments deduct; 0 while awaiting. Never the arrangement's terms (IG-29)."
          },
          "provider_cost_minor": {
            "type": "integer"
          },
          "reserve_minor": {
            "type": "integer"
          },
          "adjustments_minor": {
            "type": "integer"
          },
          "net_minor": {
            "type": "integer",
            "description": "gross_minor - platform_fee_minor - provider_cost_minor - reserve_minor + adjustments_minor; never negative."
          },
          "refunded_minor": {
            "type": "integer"
          },
          "fee_refunded_minor": {
            "type": "integer"
          },
          "state": {
            "type": "string",
            "enum": [
              "provisional",
              "available",
              "reserved_for_settlement",
              "settled",
              "disputed",
              "reversed"
            ]
          },
          "available_at": {
            "type": "string",
            "format": "date-time"
          },
          "reserve_release_at": {
            "type": "string",
            "format": "date-time"
          },
          "reserve_release_batch_id": {
            "type": "string"
          },
          "batch_id": {
            "type": "string"
          },
          "settled_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "fee_instruction_status": {
            "type": "string",
            "enum": [
              "awaiting",
              "accepted"
            ],
            "description": "IG-29: awaiting until KulOffice's complete fee instruction is accepted; a live payable is never promoted to available or batched before. Test mode is not gated (KulOffice ignores test-mode managed payments): it is promoted with a zero platform fee."
          },
          "fee_instruction_id": {
            "type": "string"
          },
          "fee_instruction_overdue_at": {
            "type": "string",
            "format": "date-time",
            "description": "When payable.fee_instruction_overdue was sent to the platform service event sink."
          }
        }
      },
      "PayableAdjustment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "payable_id": {
            "type": "string"
          },
          "payment_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "refund",
              "fee_refund",
              "reversal",
              "shortfall_to_receivable",
              "fee_adjustment"
            ]
          },
          "amount_minor": {
            "type": "integer"
          },
          "source_id": {
            "type": "string",
            "description": "The refund or reversal; for a fee_adjustment, the payment the KulOffice adjustment belongs to."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "fee_adjustment_id": {
            "type": "string",
            "description": "A fee_adjustment's KulOffice adjustment_id."
          },
          "assessment_id": {
            "type": "string"
          },
          "instruction_id": {
            "type": "string"
          }
        }
      },
      "MerchantReceivable": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "payment_id": {
            "type": "string"
          },
          "payable_id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "refund_after_settlement",
              "reversal_after_settlement",
              "payable_shortfall"
            ]
          },
          "source_id": {
            "type": "string"
          },
          "amount_minor": {
            "type": "integer"
          },
          "outstanding_minor": {
            "type": "integer"
          },
          "policy": {
            "type": "string",
            "enum": [
              "offset_future_settlements",
              "invoice"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "offset"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ServicePayment": {
        "type": "object",
        "required": [
          "provider_payment_id",
          "payment_id",
          "account_id",
          "mode",
          "status",
          "version",
          "money",
          "amount_minor",
          "currency",
          "application_id",
          "application_external_ref",
          "connector_binding_id",
          "connector_ref",
          "funds_flow",
          "last_updated_at"
        ],
        "description": "A payment as a trusted service (KulOffice) reads it back to bind its acceptance Instruction to the Gateway's evidence. No secrets and no payer data. The settlement arrangement, route and capability (IG-73) are what the payment recorded when it was routed, never the configuration in force now; each is omitted when the payment recorded none (a payment routed before they were recorded, one not routed yet, no arrangement).",
        "properties": {
          "provider_payment_id": {
            "type": "string",
            "description": "The Gateway's payment id."
          },
          "payment_id": {
            "type": "string",
            "description": "The merchant's reference (its Idempotency-Key), unique per account and mode."
          },
          "account_id": {
            "type": "string",
            "description": "The merchant account the payment belongs to."
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "status": {
            "type": "string",
            "description": "The Gateway's payment status (created, pending, requires_action, processing, authorized, succeeded, failed, cancelled, expired, refunded, partially_refunded)."
          },
          "version": {
            "type": "integer",
            "format": "int64",
            "description": "The payment row's version: it grows with every change, so a reader can order what it saw."
          },
          "money": {
            "$ref": "#/components/schemas/Money"
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64"
          },
          "currency": {
            "type": "string"
          },
          "application_id": {
            "type": "string",
            "description": "The Gateway merchant application recorded on the payment (from the key that created it, never inferred from the binding); \"\" when none."
          },
          "application_external_ref": {
            "type": "string",
            "description": "That application's immutable external_ref (KulOffice's pacapp_\u2026); \"\" when none."
          },
          "connector_binding_id": {
            "type": "string",
            "description": "The binding the payment was submitted through; \"\" for a deployment default."
          },
          "connector_ref": {
            "type": "string",
            "description": "The provider's reference."
          },
          "funds_flow": {
            "type": "string",
            "enum": [
              "collecting",
              "direct",
              "internal",
              "managed"
            ]
          },
          "settlement_arrangement_id": {
            "type": "string",
            "description": "The settlement arrangement the payment was routed under (managed or merchant-direct custody), as recorded on the payment when it was routed (IG-73); a later arrangement version changes only new payments. Omitted when none applies."
          },
          "settlement_arrangement_version": {
            "type": "integer",
            "description": "That arrangement's version. Omitted with settlement_arrangement_id."
          },
          "route": {
            "$ref": "#/components/schemas/ServicePaymentRoute"
          },
          "capability": {
            "$ref": "#/components/schemas/ServicePaymentCapability"
          },
          "settled_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 with nanoseconds: when the Gateway recorded the provider's confirmed success. Omitted until settled."
          },
          "settled_amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The settled amount in minor units. Omitted until settled."
          },
          "last_updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ServicePaymentRoute": {
        "type": "object",
        "required": [
          "operation",
          "driver",
          "method",
          "mode",
          "scope"
        ],
        "description": "The launch route the payment was submitted through (IG-73), as the routing decision recorded it: operation x connector driver x method x mode x movement scope (see GET /v1/admin/launch-matrix). Fixed once a provider may have seen the payment; a failover before that records the new target's. Omitted when the payment recorded no route.",
        "properties": {
          "operation": {
            "type": "string",
            "description": "The launch operation (`charge` for a payment)."
          },
          "driver": {
            "type": "string",
            "description": "The connector driver of the binding, or for a deployment default its implementation (`mock` for the simulated fleet)."
          },
          "method": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "on_net",
              "acc_top",
              "orch_net"
            ],
            "description": "The movement scope the route was classified as."
          }
        }
      },
      "ServicePaymentCapability": {
        "type": "object",
        "description": "What allowed the route (IG-73), as recorded when the payment was routed. Omitted when nothing was recorded (a gateway without a launch matrix, on the account's own binding).",
        "properties": {
          "launch_matrix_version": {
            "type": "string",
            "description": "The launch matrix version that enabled the route, e.g. `2026-10-12.3`."
          },
          "psp_id": {
            "type": "string",
            "description": "The PSP the payment was routed to under its capability matrix. Omitted with capability_matrix_id."
          },
          "capability_matrix_id": {
            "type": "string",
            "description": "The id of the PSP's approved capability matrix version (PSP-5.2) that allowed the payment. Omitted on the account's own binding when it is not a routed PSP's."
          },
          "capability_matrix_version": {
            "type": "integer",
            "description": "That capability matrix's version number."
          }
        }
      },
      "LaunchMatrix": {
        "type": "object",
        "description": "The launch capability matrix in force: every route that may execute (plan phase 10, \u00a716.1-2).",
        "properties": {
          "enforced": {
            "type": "boolean",
            "description": "False only on a gateway built without a matrix; off_net is refused regardless."
          },
          "off_net": {
            "type": "string",
            "enum": [
              "refused"
            ]
          },
          "version": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "operation": {
                  "type": "string"
                },
                "driver": {
                  "type": "string"
                },
                "methods": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "modes": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "scopes": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "enabled": {
                  "type": "boolean"
                },
                "legacy": {
                  "type": "boolean"
                },
                "owner_decision": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "routes": {
            "type": "array",
            "description": "Every route the rows name, expanded.",
            "items": {
              "allOf": [
                {
                  "type": "object",
                  "properties": {
                    "operation": {
                      "type": "string",
                      "enum": [
                        "charge",
                        "payout",
                        "name_lookup",
                        "incoming_transfer",
                        "bill_payment",
                        "transfer"
                      ]
                    },
                    "driver": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string"
                    },
                    "mode": {
                      "type": "string",
                      "enum": [
                        "live",
                        "test"
                      ]
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "on_net",
                        "acc_top",
                        "orch_net",
                        "off_net",
                        "legacy_payout",
                        "none"
                      ]
                    }
                  }
                },
                {
                  "type": "object",
                  "properties": {
                    "enabled": {
                      "type": "boolean"
                    },
                    "legacy": {
                      "type": "boolean"
                    }
                  }
                }
              ]
            }
          }
        }
      },
      "ServiceEventSink": {
        "type": "object",
        "description": "The platform's service event sink in one mode (IG-22): every account's payment events in that mode, in the webhook envelope (`aggregate_id` is the provider payment id), signed `KulPay-Signature: t=\u2026,v1=\u2026` with the platform secret. Not a merchant webhook endpoint: no account owns it and no merchant route shows it. Never carries the secret except `secret`, once, on creation and on a roll.",
        "required": [
          "id",
          "mode",
          "url",
          "event_types",
          "enabled",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "event_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "`payment.*` (the default) or payment webhook event types."
          },
          "enabled": {
            "type": "boolean"
          },
          "previous_secret_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "After a roll: the old secret also signs until then."
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "The signing secret: on creation and on a roll only."
          }
        }
      },
      "ApplicationProjectionPush": {
        "type": "object",
        "description": "The push of one merchant application's projection to KulOffice (IG-22): `PUT {kuloffice.base_url}/v1/integrations/kulpay-gateway/payment-acceptance/applications/{external_ref}/projection` with `{gateway_account_id, gateway_application_id, gateway_binding_id, binding_label (masked), binding_status}` and a client-credentials token. Marked pending by the event consumer when the application or a binding of its account and mode changes; sent by the `kuloffice_projection_push` job; retried with backoff on transport errors, 408, 425, 429 and 5xx (12 attempts), failed at once on any other answer.",
        "properties": {
          "application_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "external_ref": {
            "type": "string",
            "description": "KulOffice's application id, the callback's path parameter."
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "generation": {
            "type": "integer",
            "description": "Changes marked so far; a change during a send is sent again."
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_error": {
            "type": "string"
          },
          "last_status": {
            "type": "integer"
          },
          "last_body": {
            "type": "string",
            "description": "The projection KulOffice last accepted (no secret in it)."
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ServicePayable": {
        "type": "object",
        "description": "A managed pass-through payment's payable as a trusted service reads it (IG-22, IG-29): what the merchant is owed, the deductions the Gateway took (each with the settlement arrangement version it came from) and where its settlement stands. The platform fee is KulOffice's fee instruction: `fee_instruction_status` says whether it was accepted, and each `platform_fee` line echoes the `instruction_id` and `assessment_id` it deducts (a fee adjustment also its `adjustment_id`).",
        "required": [
          "payable_id",
          "account_id",
          "mode",
          "provider_payment_id",
          "payment_id",
          "currency",
          "gross_minor",
          "deductions",
          "net_minor",
          "state",
          "fee_instruction_status"
        ],
        "properties": {
          "payable_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "provider_payment_id": {
            "type": "string"
          },
          "payment_id": {
            "type": "string",
            "description": "The merchant's own reference."
          },
          "funds_flow": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "arrangement_id": {
            "type": "string"
          },
          "arrangement_version": {
            "type": "integer"
          },
          "gross_minor": {
            "type": "integer",
            "format": "int64"
          },
          "fee_instruction_status": {
            "type": "string",
            "enum": [
              "awaiting",
              "accepted"
            ],
            "description": "IG-29: awaiting until KulOffice's complete fee instruction is accepted; a live payable is never promoted to available or batched before. Test mode is not gated (KulOffice ignores test-mode managed payments): it is promoted with a zero platform fee."
          },
          "fee_instruction_id": {
            "type": "string",
            "description": "The accepted instruction."
          },
          "deductions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "kind",
                "amount_minor",
                "arrangement_id",
                "arrangement_version"
              ],
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "platform_fee",
                    "provider_cost",
                    "reserve",
                    "adjustment"
                  ],
                  "description": "platform_fee: one line per merchant-borne settlement_deduction line of the accepted fee instruction (one line of 0 for an explicit zero or while awaiting); provider_cost; reserve; then adjustment lines: one per KulOffice fee adjustment that changed the deduction before batching or was carried onto this payable (adjustment_kind fee_adjustment, with instruction_id, assessment_id and fee_adjustment_id), and one per other payable adjustment."
                },
                "amount_minor": {
                  "type": "integer",
                  "format": "int64",
                  "description": "Taken from the gross: positive deducts, negative gives back. `net_minor` = `gross_minor` - the sum of the lines."
                },
                "arrangement_id": {
                  "type": "string"
                },
                "arrangement_version": {
                  "type": "integer"
                },
                "adjustment_kind": {
                  "type": "string",
                  "enum": [
                    "refund",
                    "fee_refund",
                    "reversal",
                    "shortfall_to_receivable",
                    "fee_adjustment"
                  ]
                },
                "adjustment_id": {
                  "type": "string",
                  "description": "The Gateway's own id of the adjustment."
                },
                "fee_adjustment_id": {
                  "type": "string",
                  "description": "For adjustment_kind fee_adjustment: KulOffice's adjustment id."
                },
                "source_id": {
                  "type": "string",
                  "description": "The refund or reversal behind an adjustment; for a fee_adjustment carried from a batched payable, the payment it belongs to."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "instruction_id": {
                  "type": "string",
                  "description": "The KulOffice fee instruction a platform_fee or fee_adjustment line comes from."
                },
                "assessment_id": {
                  "type": "string",
                  "description": "The KulOffice fee assessment a platform_fee or fee_adjustment line deducts."
                }
              }
            }
          },
          "net_minor": {
            "type": "integer",
            "format": "int64"
          },
          "state": {
            "type": "string",
            "enum": [
              "provisional",
              "available",
              "reserved_for_settlement",
              "settled",
              "disputed",
              "reversed"
            ]
          },
          "available_at": {
            "type": "string",
            "format": "date-time"
          },
          "batch_id": {
            "type": "string",
            "description": "The settlement batch, once batched."
          },
          "settled_at": {
            "type": "string",
            "format": "date-time",
            "description": "Once its batch was recorded settled."
          },
          "reserve_release_at": {
            "type": "string",
            "format": "date-time"
          },
          "reserve_release_batch_id": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FeeInstructionLine": {
        "type": "object",
        "description": "One fee assessment KulOffice recognized for the payment (IG-29). Every line is stored as evidence; only bearer merchant with collection_method settlement_deduction deducts from the payable. provider_split lines are evidence only.",
        "required": [
          "assessment_id",
          "component_code",
          "kind",
          "bearer",
          "collection_method",
          "amount_minor",
          "recognized_at"
        ],
        "properties": {
          "assessment_id": {
            "type": "string",
            "maxLength": 64,
            "description": "Unique within the instruction."
          },
          "component_code": {
            "type": "string",
            "maxLength": 64
          },
          "kind": {
            "type": "string",
            "maxLength": 32,
            "description": "KulOffice's component kind (platform_fee, provider_cost, tax, adjustment)."
          },
          "bearer": {
            "type": "string",
            "enum": [
              "customer",
              "merchant",
              "platform"
            ]
          },
          "collection_method": {
            "type": "string",
            "enum": [
              "settlement_deduction",
              "internal_balance_debit",
              "service_invoice",
              "provider_split",
              "platform_absorption"
            ]
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "recognized_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "FeeInstructionRequest": {
        "type": "object",
        "description": "A payment's one complete fee instruction (IG-29). `lines` is required: an empty array is the explicit zero fee, absence is never zero.",
        "required": [
          "instruction_id",
          "provider_payment_id",
          "transaction_id",
          "pricing_capture_id",
          "plan_version_id",
          "currency",
          "complete",
          "lines"
        ],
        "properties": {
          "instruction_id": {
            "type": "string",
            "maxLength": 64
          },
          "provider_payment_id": {
            "type": "string",
            "description": "The payment of the path."
          },
          "transaction_id": {
            "type": "string",
            "maxLength": 100,
            "description": "KulOffice's Transaction of the payment."
          },
          "pricing_capture_id": {
            "type": "string",
            "maxLength": 100
          },
          "plan_version_id": {
            "type": "string",
            "maxLength": 100
          },
          "currency": {
            "type": "string",
            "description": "The payment's currency."
          },
          "complete": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Must be true: a partial instruction is refused."
          },
          "lines": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/FeeInstructionLine"
            }
          }
        }
      },
      "FeeInstruction": {
        "allOf": [
          {
            "$ref": "#/components/schemas/FeeInstructionRequest"
          },
          {
            "type": "object",
            "required": [
              "account_id",
              "mode",
              "deduction_minor",
              "effect",
              "accepted_at"
            ],
            "properties": {
              "account_id": {
                "type": "string"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "live",
                  "test"
                ]
              },
              "payable_id": {
                "type": "string"
              },
              "deduction_minor": {
                "type": "integer",
                "format": "int64",
                "description": "The platform_fee deduction it set: the sum of its merchant settlement_deduction lines; 0 when record only."
              },
              "effect": {
                "type": "string",
                "enum": [
                  "deducted",
                  "record_only"
                ],
                "description": "record_only: the payable was refunded or reversed in full before the instruction, which is kept as evidence and deducts nothing."
              },
              "accepted_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ],
        "description": "The accepted instruction, as stored, and what it did."
      },
      "FeeAdjustmentRequest": {
        "type": "object",
        "required": [
          "adjustment_id",
          "assessment_id",
          "amount_minor"
        ],
        "properties": {
          "adjustment_id": {
            "type": "string",
            "maxLength": 64,
            "description": "Idempotency: the same adjustment again returns the stored one."
          },
          "instruction_id": {
            "type": "string",
            "maxLength": 64,
            "description": "The payment's accepted instruction, when sent."
          },
          "assessment_id": {
            "type": "string",
            "description": "An assessment of the payment's accepted instruction."
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64",
            "description": "Signed, not 0: positive raises the fee, negative lowers it."
          },
          "currency": {
            "type": "string",
            "description": "The payment's currency, when sent."
          },
          "cause": {
            "type": "string",
            "maxLength": 200,
            "description": "Why KulOffice adjusted the assessment; evidence."
          },
          "recognized_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "description": "adjustment_id, assessment_id and amount_minor are required; instruction_id and currency, when sent, must be the payment's; cause and recognized_at are stored as evidence."
      },
      "FeeAdjustment": {
        "type": "object",
        "required": [
          "id",
          "adjustment_id",
          "provider_payment_id",
          "assessment_id",
          "amount_minor",
          "instruction_id",
          "effect",
          "accepted_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The Gateway's own id of the adjustment (the payable read's adjustment_id)."
          },
          "adjustment_id": {
            "type": "string",
            "description": "KulOffice's adjustment id (the payable read's fee_adjustment_id)."
          },
          "provider_payment_id": {
            "type": "string"
          },
          "assessment_id": {
            "type": "string"
          },
          "amount_minor": {
            "type": "integer",
            "format": "int64"
          },
          "instruction_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "currency": {
            "type": "string"
          },
          "payable_id": {
            "type": "string"
          },
          "effect": {
            "type": "string",
            "enum": [
              "platform_fee",
              "carried",
              "record_only"
            ],
            "description": "platform_fee: changed the deduction of a payable not yet batched. carried: the payable was batched, settled or reversed; the adjustment becomes a fee_adjustment line on the merchant's next batched payable (target_payable_id once applied). record_only: the assessment is not a merchant settlement deduction, or the instruction deducted nothing."
          },
          "target_payable_id": {
            "type": "string"
          },
          "applied_at": {
            "type": "string",
            "format": "date-time"
          },
          "accepted_at": {
            "type": "string",
            "format": "date-time"
          },
          "cause": {
            "type": "string"
          },
          "recognized_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CustodyAccount": {
        "type": "object",
        "description": "An external account behind a ledger custody pool (IG-71): a provider account read through a connector binding's balance read, or a bank account whose statements are imported (live only).",
        "properties": {
          "id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "currency": {
            "type": "string"
          },
          "ledger_code": {
            "type": "string",
            "enum": [
              "client_funds",
              "provider_custody"
            ]
          },
          "connector_binding_id": {
            "type": "string"
          },
          "statement_account": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "created_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CustodyReconciliation": {
        "type": "object",
        "description": "One comparison of a ledger custody pool (mode, currency, ledger code, across tenants) with its external balances as of `cutoff` (IG-71). Written once; a rerun at the same cutoff returns it.",
        "properties": {
          "id": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "ledger_code": {
            "type": "string"
          },
          "cutoff": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "matched",
              "difference",
              "unverifiable"
            ]
          },
          "ledger_minor": {
            "type": "integer",
            "description": "The pool's ledger balance (debits less credits of the journal lines posted at or before the cutoff)."
          },
          "external_minor": {
            "type": "integer",
            "description": "The sum of the readings; 0 when unverifiable."
          },
          "difference_minor": {
            "type": "integer",
            "description": "External less ledger; negative when the ledger shows more than is held."
          },
          "readings": {
            "type": "string",
            "description": "JSON array, one reading per custody account: custody_account_id, label, connector_binding_id, statement_account, source (provider_balance, statement or none), balance_minor, as_of, statement_file_id, parse_version, provider_reference, unverifiable (why there is no reading)."
          },
          "reason": {
            "type": "string",
            "description": "Why the result is unverifiable."
          },
          "exception_id": {
            "type": "string",
            "description": "The custody_difference reconciliation exception a difference opened."
          },
          "run_by": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ServiceSettlement": {
        "type": "object",
        "description": "A settlement batch as a trusted service reads it (IG-80).",
        "required": [
          "batch_id",
          "account_id",
          "mode",
          "funds_flow",
          "currency",
          "status",
          "gross_minor",
          "fees_minor",
          "net_minor",
          "external_reference",
          "lines"
        ],
        "properties": {
          "batch_id": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "funds_flow": {
            "type": "string",
            "description": "collecting, direct or managed."
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "The batch status: created, ready, paying, paid, settled, failed, carried_forward or recorded."
          },
          "gross_minor": {
            "type": "integer",
            "format": "int64"
          },
          "fees_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The fee taken, positive; a fee given back in the batch lowers it."
          },
          "net_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The batch's signed net: gross less fees, plus refunds, adjustments and carried amounts."
          },
          "external_reference": {
            "type": "string",
            "description": "The operators' evidence of the external settlement; empty until settled."
          },
          "settled_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absent until settled."
          },
          "lines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServiceSettlementLine"
            }
          }
        }
      },
      "ServiceSettlementLine": {
        "type": "object",
        "description": "A payment's movements in a batch.",
        "required": [
          "provider_payment_id",
          "gross_minor",
          "fee_minor",
          "fee_instruction_id",
          "net_minor"
        ],
        "properties": {
          "provider_payment_id": {
            "type": "string"
          },
          "gross_minor": {
            "type": "integer",
            "format": "int64"
          },
          "fee_minor": {
            "type": "integer",
            "format": "int64",
            "description": "The fee taken, positive; negative when the batch gives fee back."
          },
          "fee_instruction_id": {
            "type": "string",
            "description": "KulOffice's accepted fee instruction for the payment; empty for none."
          },
          "net_minor": {
            "type": "integer",
            "format": "int64",
            "description": "All the payment's movements in the batch."
          }
        }
      }
    }
  }
}
