{
  "openapi": "3.0.3",
  "info": {
    "title": "Nucleus verified local Transfer and consented account-read profile",
    "version": "2026-11-07-local",
    "description": "Scoped frontend handoff for the actual local NGN/NPS zero-fee Transfer profile, durable notification visibility, and consented read-only open banking. The global Transfer target is broader and institution-configured; this profile does not advertise other rails, currencies, live provider certification, live SMS/email delivery or the whole legacy core API. Existing ledger is retained. Auth principal separation, signed booked settlement evidence and real two-tenant isolation were exercised by the local production-JAR journey. Field names and timestamp encodings reflect current resources rather than the older raw API export. This is an implemented, source-reviewed contract with journey-level and focused-test evidence, not a claim that all 34 operations individually passed HTTP conformance. Each operation states that evidence boundary; retry/recovery and metadata/list operations without a direct journey call remain source-reviewed contracts.",
    "x-evidence": [
      "corebanking/nucleus-server/src/localJourneyTest/java/com/nucleus/core/payments/PaymentLocalJourneyTest.java",
      "corebanking/nucleus-server/src/localJourneyTest/java/com/nucleus/core/openbanking/OpenBankingLocalJourney.java",
      "corebanking/nucleus-server/build/local-banking-journey/evidence.json",
      "corebanking/nucleus-server/build/local-banking-journey/tier-one-evidence.json"
    ]
  },
  "servers": [
    {
      "url": "/nucleus/api/v1",
      "description": "Actual engine API root; choose the institution deployment host separately."
    }
  ],
  "tags": [
    {
      "name": "Transfer",
      "description": "Capability name: Transfer. This document covers the local NGN/NPS profile only."
    },
    {
      "name": "Open banking",
      "description": "Tenant-local partners with explicit customer-granted read-only account access."
    }
  ],
  "paths": {
    "/payment-transfers/routes": {
      "post": {
        "operationId": "createLocalTransferRoute",
        "summary": "Configure a local Transfer route",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Local NGN/NPS zero-fee profile. Starts DRAFT; explicit clearing and financial mappings are validated. Institution identity must match configured institution.",
        "x-permission": "CREATE_PAYMENTCONFIG",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RouteInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/routes/{id}/approve": {
      "post": {
        "operationId": "approveLocalTransferRoute",
        "summary": "Approve a route independently",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Route"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "A different operator must approve the route maker. Configuration identity and financial mappings are immutable.",
        "x-permission": "APPROVE_PAYMENTCONFIG",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/payees": {
      "post": {
        "operationId": "enquireLocalTransferPayee",
        "summary": "Request exact beneficiary confirmation",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payee"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Route authority required. At most 100 enquiries per route/minute. Transport receipt alone does not establish CONFIRMED.",
        "x-permission": "READ_PAYMENTTRANSFER",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayeeInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/payees/{id}": {
      "get": {
        "operationId": "readLocalTransferPayee",
        "summary": "Read scoped beneficiary confirmation",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payee"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests": {
      "post": {
        "operationId": "createLocalTransfer",
        "summary": "Create an unfunded Transfer instruction",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "CREATE_PAYMENTTRANSFER",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/approve": {
      "post": {
        "operationId": "approveLocalTransfer",
        "summary": "Approve and fund through the retained ledger",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "410": {
            "$ref": "#/components/responses/Error410"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Different operator from maker, configured route/office/amount authority, live payee confirmation, approval deadline and institution customer daily limits required. Funding and durable outbox intent commit atomically. Uncertain transport outcome retains original identity and funding.",
        "x-permission": "APPROVE_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}": {
      "get": {
        "operationId": "readLocalTransfer",
        "summary": "Read scoped Transfer state",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/history": {
      "get": {
        "operationId": "readLocalTransferHistory",
        "summary": "Read Transfer milestone history",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/HistoryRow"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/notifications": {
      "get": {
        "operationId": "readLocalOutgoingNotifications",
        "summary": "Read masked outgoing notification delivery state",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationRow"
                  },
                  "maxItems": 200
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Existing exact payment read/route/office authority is checked before notification lookup. Recovery reads remain available when new-payment licensing/module gates are off. ACCEPTED denotes HTTP transport acceptance only.",
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/incoming/{id}/notifications": {
      "get": {
        "operationId": "readLocalIncomingNotifications",
        "summary": "Read masked incoming notification delivery state",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NotificationRow"
                  },
                  "maxItems": 200
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Existing exact payment read/route/office authority is checked before notification lookup. Recovery reads remain available when new-payment licensing/module gates are off. ACCEPTED denotes HTTP transport acceptance only.",
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/settle": {
      "post": {
        "operationId": "settleLocalOutgoingTransfer",
        "summary": "Settle completed outgoing Transfer",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "An operator independent of maker and approver must supply a matching, previously received signed BOOK settlement statement identity. Exact amount/direction/currency/correlation required. Consumes evidence with the retained ledger posting; a typed reference alone is insufficient.",
        "x-permission": "RECONCILE_PAYMENTTRANSFER",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettlementInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/reconcile": {
      "post": {
        "operationId": "reconcileLocalOutgoingTransfer",
        "summary": "Reconcile settled or compensated Transfer",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Independent operator; SETTLED or REVERSED and no unresolved provider work required.",
        "x-permission": "RECONCILE_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/incoming/{id}": {
      "get": {
        "operationId": "readLocalIncomingTransfer",
        "summary": "Read scoped incoming processing/credit state",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingPayment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/incoming/{id}/settle": {
      "post": {
        "operationId": "settleLocalIncomingTransfer",
        "summary": "Settle an actual incoming customer credit",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Customer credit must already exist. Exact signed BOOK CRDT statement evidence is consumed atomically with asset/receivable settlement.",
        "x-permission": "RECONCILE_PAYMENTTRANSFER",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettlementInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/incoming/{id}/reconcile": {
      "post": {
        "operationId": "reconcileLocalIncomingTransfer",
        "summary": "Reconcile settled incoming payment",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Requires settlement evidence and completed confirmation reply work.",
        "x-permission": "RECONCILE_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/enquire": {
      "post": {
        "operationId": "enquireLocalTransferOutcome",
        "summary": "Query original unresolved external outcome",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Only SUBMITTED/UNKNOWN. Reuses immutable original instruction identity; never initiates another debit.",
        "x-permission": "PROCESS_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/exceptions": {
      "get": {
        "operationId": "readLocalTransferExceptions",
        "summary": "Read scoped unresolved work",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Exceptions"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "x-permission": "READ_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/incoming/{id}/retry": {
      "post": {
        "operationId": "retryLocalIncomingProcessing",
        "summary": "Retry parked uncommitted incoming processing",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncomingState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Only PARKED processing can be rearmed. REJECTED is terminal for that original message and is not replayed as a new credit.",
        "x-permission": "PROCESS_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/requests/{id}/retry-reversal": {
      "post": {
        "operationId": "retryLocalTransferCompensation",
        "summary": "Retry pending compensating credit",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Transfer"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Only REVERSAL_PENDING. Reuses retained signed rejection and original funding lineage; does not initiate another provider submission.",
        "x-permission": "PROCESS_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/process": {
      "post": {
        "operationId": "processLocalTransferWork",
        "summary": "Run existing durable payment and alert work",
        "tags": [
          "Transfer"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProcessResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          }
        },
        "description": "Controlled operator recovery endpoint. Worker impersonates the internal system user for retained ledger posting only; caller remains subject to staff permission checks.",
        "x-permission": "PROCESS_PAYMENTTRANSFER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/payment-transfers/nps/messages": {
      "post": {
        "operationId": "receiveLocalSignedNpsMessage",
        "summary": "Receive pinned signed/encrypted local XML",
        "tags": [
          "Transfer"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Mandatory whole-document signature verification and business-content decryption using the configured participant key and pinned peer key. No Basic/Bearer identity substitutes for XML authentication. Numeric loopback/local-tenant configuration only. Handles local acmt.023/.024, pacs.008/.002/.028 and camt.053 layouts; no provider certification is asserted. Repeated message identity with changed business payload conflicts; HTTP 200 is durable receipt, not credit/completion.",
        "requestBody": {
          "required": true,
          "content": {
            "application/xml": {
              "schema": {
                "type": "string",
                "maxLength": 262144
              }
            }
          }
        },
        "x-authentication": {
          "mode": "pinned-signed-encrypted-xml",
          "profile": "infrastructure/rails/nps/LOCAL_PROFILE.md"
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/payments/PaymentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/partners": {
      "post": {
        "operationId": "registerLocalOpenBankingPartner",
        "summary": "Register a tenant-local read partner",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerSecret"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "x-permission": "CREATE_OPENBANKING_PARTNER",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PartnerRegistration"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingManagementApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/partners/{clientId}/suspend": {
      "post": {
        "operationId": "suspendLocalOpenBankingPartner",
        "summary": "Suspend partner access immediately",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "x-permission": "UPDATE_OPENBANKING_PARTNER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingManagementApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/partners/{clientId}/rotate-secret": {
      "post": {
        "operationId": "rotateLocalOpenBankingPartnerSecret",
        "summary": "Rotate partner secret and revoke outstanding authorization",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "StaffBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "clientId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartnerSecret"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "One-time returned raw secret. Atomically replaces hash, revokes every partner token family and invalidates outstanding authorization codes. Creates credential-free append-only audit evidence.",
        "x-permission": "UPDATE_OPENBANKING_PARTNER",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingManagementApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/self/openbanking/consents": {
      "post": {
        "operationId": "grantLocalOpenBankingConsent",
        "summary": "Grant explicit owned-account read consent",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "CustomerBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Consent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Requires self-service principal and explicit client mapping. A staff account is refused even if it has ALL_FUNCTIONS. Consent is separate from purpose-only privacy consent records.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConsentGrant"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/SelfOpenBankingConsentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      },
      "get": {
        "operationId": "listLocalOpenBankingConsents",
        "summary": "List mapped customer consents",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "CustomerBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "customerId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Consent"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/SelfOpenBankingConsentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/self/openbanking/consents/{id}/authorize": {
      "post": {
        "operationId": "authorizeLocalOpenBankingConsent",
        "summary": "Issue a single-use S256 code",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "CustomerBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizationCode"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AuthorizationInput"
              }
            }
          }
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/SelfOpenBankingConsentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/self/openbanking/consents/{id}/revoke": {
      "post": {
        "operationId": "revokeLocalOpenBankingConsent",
        "summary": "Withdraw customer delegated authority",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "CustomerBasic": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Revoked"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Mapped owning customer only. Existing linked access and refresh tokens immediately fail consent checks.",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/SelfOpenBankingConsentApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/oauth/token": {
      "post": {
        "operationId": "exchangeLocalOpenBankingToken",
        "summary": "Exchange partner credentials and grant",
        "tags": [
          "Open banking"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenPair"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "description": "Known partner authentication attempt budget exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds before retry; current implementation returns 60."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Authenticated by required client_id/client_secret FORM fields, not anonymous access and not staff Basic. Known partner attempt budget is persisted and committed before BCrypt (default 120/minute); 429 includes Retry-After. Tenant is only a routing hint: credentials, codes, grants and token families must exist in that tenant database. Invalid grant is 400; invalid partner is 401.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthExchange"
              }
            }
          }
        },
        "x-authentication": {
          "mode": "client-secret-form",
          "requiredFields": [
            "client_id",
            "client_secret"
          ]
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingOAuthApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/oauth/revoke": {
      "post": {
        "operationId": "revokeLocalOpenBankingToken",
        "summary": "Exchange partner credentials and revoke token family",
        "tags": [
          "Open banking"
        ],
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Revoked"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "no-store"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "description": "Known partner authentication attempt budget exceeded.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "Seconds before retry; current implementation returns 60."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Authenticated by required client_id/client_secret FORM fields, not anonymous access and not staff Basic. Known partner attempt budget is persisted and committed before BCrypt (default 120/minute); 429 includes Retry-After. Tenant is only a routing hint: credentials, codes, grants and token families must exist in that tenant database. Invalid grant is 400; invalid partner is 401.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthRevoke"
              }
            }
          }
        },
        "x-authentication": {
          "mode": "client-secret-form",
          "requiredFields": [
            "client_id",
            "client_secret"
          ]
        },
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingOAuthApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/meta": {
      "get": {
        "operationId": "readLocalOpenBankingMetadata",
        "summary": "Read delegated read-profile metadata",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metadata"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Requires accounts:read, active partner/consent, module and partner_api licence. External certification is not asserted.",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/accounts": {
      "get": {
        "operationId": "listLocalOpenBankingAccounts",
        "summary": "List explicitly consented real accounts",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObAccounts"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Requires accounts:read. Opaque partner token never becomes a staff AppUser. Consent, expiry, partner, family and tenant isolation checked on every read.",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/accounts/{accountId}/balance": {
      "get": {
        "operationId": "readLocalOpenBankingBalance",
        "summary": "Read a consented account balance",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "accountId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObBalances"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Requires balances:read plus account allowlist and customer ownership. Unconsented or other-customer account returns 404.",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    },
    "/openbanking/accounts/{accountId}/transactions": {
      "get": {
        "operationId": "readLocalOpenBankingTransactions",
        "summary": "Read consented booked transactions",
        "tags": [
          "Open banking"
        ],
        "security": [
          {
            "PartnerBearer": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Tenant"
          },
          {
            "name": "accountId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          },
          {
            "name": "fromBookingDateTime",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive booking-date filter; offset datetime is reduced to its LocalDate."
          },
          {
            "name": "toBookingDateTime",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Inclusive booking-date filter; offset datetime is reduced to its LocalDate."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObTransactions"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          }
        },
        "description": "Requires transactions:read plus explicit account/customer scope. Module/partner_api licence applies to every read.",
        "x-evidence": {
          "contract": "SOURCE_REVIEWED_IMPLEMENTED",
          "source": "corebanking/nucleus-server/src/main/java/com/nucleus/core/openbanking/api/OpenBankingApiResource.java",
          "qualification": "Selected HTTP operations participate in the local production-JAR journey; focused tests cover additional invariants. Individual full HTTP conformance of this operation is not asserted by this document."
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Tenant": {
        "name": "Nucleus-Platform-TenantId",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{1,128}$"
        },
        "description": "Existing institution tenant. A routing header never grants data access; authentication and authorization must resolve within that tenant."
      }
    },
    "securitySchemes": {
      "StaffBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "Authenticated bank staff, platform permission plus configured route/office/customer/amount authority. Self-service users and opaque partner tokens are refused."
      },
      "CustomerBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "Mapped self-service customer principal. Customer consent cannot be granted by staff permissions."
      },
      "PartnerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque tenant-local reference token",
        "description": "Read-only consent/account/scopes/expiry/family checked on every request. Never accepted as staff authentication."
      }
    },
    "responses": {
      "Error400": {
        "description": "Invalid request, grant or domain rule.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error401": {
        "description": "Authentication/tenant/token invalid; filter-level principal separation can return 401.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error403": {
        "description": "Permission, customer mapping, route/office/amount authority, scope, licence or policy refused.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error404": {
        "description": "Scoped resource, account or original correlation unavailable.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error409": {
        "description": "Identity/payload/state/correlation/settlement conflict.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error410": {
        "description": "Approval window or beneficiary confirmation expired.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error429": {
        "description": "Configured attempt/enquiry budget exceeded.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Error503": {
        "description": "Required local module or transport configuration unavailable.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Amount": {
        "type": "number",
        "description": "JSON decimal number. Do not calculate money using binary floating point."
      },
      "AmountInput": {
        "oneOf": [
          {
            "type": "number",
            "minimum": 0,
            "exclusiveMinimum": true,
            "multipleOf": 0.01,
            "maximum": 9999999999999.99
          },
          {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]{1,2})?$"
          }
        ],
        "description": "Positive decimal principal, maximum 13 integer digits and two fractional digits. JSON numbers and decimal strings are accepted; DECIMAL(19,6) storage bounds still apply."
      },
      "AuthorityInput": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "officeId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "maxAmount": {
            "$ref": "#/components/schemas/AmountInput"
          },
          "canCreate": {
            "type": "boolean"
          },
          "canApprove": {
            "type": "boolean"
          },
          "canReconcile": {
            "type": "boolean"
          }
        },
        "required": [
          "userId",
          "officeId",
          "maxAmount"
        ],
        "description": "Named enabled staff operator; route creation requires distinct users. Each action also requires its platform permission and office scope."
      },
      "AuthorizationCode": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "redirectUri": {
            "type": "string",
            "format": "uri"
          },
          "expiresIn": {
            "type": "integer",
            "format": "int64",
            "enum": [
              120
            ]
          }
        },
        "required": [
          "code",
          "redirectUri",
          "expiresIn"
        ],
        "description": "Returned as JSON to the authenticated customer flow; the endpoint does not perform an HTTP redirect. Code is hashed at rest, single-use, exact-redirect and S256-bound."
      },
      "AuthorizationInput": {
        "type": "object",
        "properties": {
          "redirectUri": {
            "type": "string",
            "format": "uri"
          },
          "codeChallenge": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          },
          "codeChallengeMethod": {
            "type": "string",
            "enum": [
              "S256"
            ]
          }
        },
        "required": [
          "redirectUri",
          "codeChallenge",
          "codeChallengeMethod"
        ]
      },
      "Consent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "client_id": {
            "type": "string"
          },
          "customer_id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "scopes": {
            "type": "string",
            "description": "Space-separated scope string on the JDBC response."
          },
          "expires_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "created_by": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "created_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "revoked_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "accountIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            }
          }
        },
        "required": [
          "id",
          "client_id",
          "customer_id",
          "scopes",
          "expires_at",
          "created_by",
          "created_at",
          "accountIds"
        ],
        "description": "Response uses persisted snake_case column names plus accountIds, unlike the camelCase grant request."
      },
      "ConsentGrant": {
        "type": "object",
        "properties": {
          "customerId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "clientId": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "minItems": 1,
            "uniqueItems": true
          },
          "accountIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64",
              "minimum": 1
            },
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Future instant, at most 90 days from grant."
          }
        },
        "required": [
          "customerId",
          "clientId",
          "scopes",
          "accountIds",
          "expiresAt"
        ],
        "description": "Authenticated mapped customer must own every active savings account. Staff permissions never substitute for customer consent."
      },
      "DbTimestamp": {
        "type": "string",
        "format": "date-time",
        "description": "JDBC Date/Timestamp values use the engine ISO_INSTANT JSON converter."
      },
      "Error": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "code": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer",
                "format": "int64"
              }
            ]
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {},
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": true,
        "description": "Framework, domain, policy and OAuth errors have distinct payloads. Read status plus problem code/detail; do not depend on one legacy error envelope. Filter denials may be 401 or 403. Unknown provider outcome is not permission to create another Transfer reference."
      },
      "Exceptions": {
        "type": "object",
        "properties": {
          "outgoing": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutgoingException"
            },
            "maxItems": 100
          },
          "incoming": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IncomingException"
            },
            "maxItems": 100
          },
          "incomingOwner": {
            "type": "string",
            "enum": [
              "PAYMENT_OPERATIONS"
            ]
          }
        },
        "required": [
          "outgoing",
          "incoming",
          "incomingOwner"
        ]
      },
      "HistoryRow": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "actor_id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "evidence_id": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "occurred_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          }
        },
        "required": [
          "status",
          "actor_id",
          "occurred_at"
        ]
      },
      "IncomingException": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "status": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN"
            ]
          },
          "received_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "exception_code": {
            "type": "string"
          }
        }
      },
      "IncomingPayment": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "route_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "PACS008"
            ]
          },
          "original_id": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "end_to_end_id": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "debtor_agent": {
            "type": "string"
          },
          "creditor_agent": {
            "type": "string"
          },
          "debtor_account": {
            "type": "string"
          },
          "creditor_account": {
            "type": "string"
          },
          "account_name": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN"
            ]
          },
          "result_status": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          },
          "payload_hash": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSED",
              "PARKED",
              "REJECTED",
              "RECONCILED"
            ]
          },
          "savings_id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "transaction_id": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "accounting_event_id": {
            "type": "string"
          },
          "exception_code": {
            "type": "string"
          },
          "received_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "attempts": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "next_attempt_at": {
            "$ref": "#/components/schemas/DbTimestamp"
          }
        },
        "required": [
          "message_id",
          "route_id",
          "kind",
          "amount",
          "currency",
          "status",
          "received_at"
        ],
        "description": "Current scoped JDBC inbox projection; uses snake_case. Acceptance and customer credit are separate. Missing customer binding returns 404 rather than an unscoped incoming read."
      },
      "IncomingState": {
        "type": "object",
        "properties": {
          "messageId": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "SETTLED",
              "RECONCILED"
            ]
          },
          "accountingEventId": {
            "type": "string"
          }
        },
        "required": [
          "messageId",
          "status"
        ]
      },
      "InstantEpochSeconds": {
        "type": "number",
        "description": "Java Instant on the current HTTP wire: Unix epoch seconds, potentially fractional. Observed in the verified Transfer deadline response. This is not an ISO date-time string."
      },
      "Metadata": {
        "type": "object",
        "properties": {
          "apiVersion": {
            "type": "string",
            "enum": [
              "1.0"
            ]
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "accounts",
                "balances",
                "transactions"
              ]
            }
          },
          "authentication": {
            "type": "string"
          },
          "certification": {
            "type": "string"
          }
        },
        "required": [
          "apiVersion",
          "capabilities",
          "authentication",
          "certification"
        ]
      },
      "NotificationRow": {
        "type": "object",
        "properties": {
          "notificationId": {
            "type": "string",
            "format": "uuid"
          },
          "eventType": {
            "type": "string",
            "enum": [
              "INCOMING_ACCEPTED",
              "INCOMING_CREDITED",
              "INCOMING_REJECTED",
              "OUTGOING_FUNDED",
              "OUTGOING_COMPLETED",
              "OUTGOING_REVERSED",
              "ACCOUNT_CREDITED",
              "ACCOUNT_DEBITED"
            ]
          },
          "maskedAccount": {
            "type": "string",
            "pattern": "^\\*{4}.*$"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "ledgerTransactionId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "createdAt": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "channel": {
            "type": "string",
            "enum": [
              "EMAIL",
              "SMS"
            ]
          },
          "deliveryStatus": {
            "type": "string",
            "enum": [
              "PENDING",
              "ACCEPTED",
              "SUPPRESSED"
            ]
          },
          "attempts": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "lastCode": {
            "type": "string"
          },
          "nextAttemptAt": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "acceptedAt": {
            "$ref": "#/components/schemas/DbTimestamp"
          }
        },
        "required": [
          "notificationId",
          "eventType",
          "maskedAccount",
          "amount",
          "currency",
          "createdAt"
        ],
        "description": "One logical milestone/channel row, at most 200 rows. No recipient, provider idempotency key or secret is exposed. ACCEPTED means the configured HTTP transport accepted delivery, not that the customer received/read SMS or email. INCOMING_REJECTED has no ledgerTransactionId and never implies credit or refund. Null left-join fields can be omitted."
      },
      "OAuthCodeExchange": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          },
          "grant_type": {
            "type": "string",
            "enum": [
              "authorization_code"
            ]
          },
          "code": {
            "type": "string"
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri"
          },
          "code_verifier": {
            "type": "string",
            "pattern": "^[A-Za-z0-9._~-]{43,128}$"
          }
        },
        "required": [
          "client_id",
          "client_secret",
          "grant_type",
          "code",
          "redirect_uri",
          "code_verifier"
        ]
      },
      "OAuthExchange": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/OAuthCodeExchange"
          },
          {
            "$ref": "#/components/schemas/OAuthRefreshExchange"
          }
        ]
      },
      "OAuthRefreshExchange": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          },
          "grant_type": {
            "type": "string",
            "enum": [
              "refresh_token"
            ]
          },
          "refresh_token": {
            "type": "string"
          }
        },
        "required": [
          "client_id",
          "client_secret",
          "grant_type",
          "refresh_token"
        ]
      },
      "OAuthRevoke": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          },
          "token": {
            "type": "string"
          }
        },
        "required": [
          "client_id",
          "client_secret"
        ],
        "description": "Unknown token has the same successful revocation response. Known token revokes its entire family. No Basic or Bearer header replaces these required partner form credentials."
      },
      "ObAccount": {
        "type": "object",
        "properties": {
          "AccountId": {
            "type": "string"
          },
          "Currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          },
          "AccountType": {
            "type": "string"
          },
          "Account": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "Identification": {
                  "type": "string"
                }
              },
              "required": [
                "Identification"
              ]
            }
          }
        },
        "required": [
          "AccountId",
          "Currency",
          "AccountType",
          "Account"
        ]
      },
      "ObAccounts": {
        "type": "object",
        "properties": {
          "Data": {
            "type": "object",
            "properties": {
              "Account": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ObAccount"
                }
              }
            },
            "required": [
              "Account"
            ]
          }
        },
        "required": [
          "Data"
        ]
      },
      "ObAmount": {
        "type": "object",
        "properties": {
          "Amount": {
            "type": "string",
            "pattern": "^[0-9]+(\\.[0-9]+)?$"
          },
          "Currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$"
          }
        },
        "required": [
          "Amount",
          "Currency"
        ]
      },
      "ObBalance": {
        "type": "object",
        "properties": {
          "AccountId": {
            "type": "string"
          },
          "Type": {
            "type": "string",
            "enum": [
              "InterimBooked",
              "InterimAvailable"
            ]
          },
          "Amount": {
            "$ref": "#/components/schemas/ObAmount"
          },
          "CreditDebitIndicator": {
            "type": "string",
            "enum": [
              "Credit",
              "Debit"
            ]
          }
        },
        "required": [
          "AccountId",
          "Type",
          "Amount",
          "CreditDebitIndicator"
        ]
      },
      "ObBalances": {
        "type": "object",
        "properties": {
          "Data": {
            "type": "object",
            "properties": {
              "Balance": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ObBalance"
                },
                "minItems": 2,
                "maxItems": 2
              }
            },
            "required": [
              "Balance"
            ]
          }
        },
        "required": [
          "Data"
        ],
        "description": "Real booked and available balances. Amount is an absolute decimal string; the indicator carries the sign. Available uses retained savings hold calculations."
      },
      "ObTransaction": {
        "type": "object",
        "properties": {
          "AccountId": {
            "type": "string"
          },
          "TransactionId": {
            "type": "string"
          },
          "Amount": {
            "$ref": "#/components/schemas/ObAmount"
          },
          "CreditDebitIndicator": {
            "type": "string",
            "enum": [
              "Credit",
              "Debit"
            ]
          },
          "Status": {
            "type": "string",
            "enum": [
              "Booked"
            ]
          },
          "BookingDate": {
            "type": "string",
            "format": "date"
          },
          "TransactionInformation": {
            "type": "string"
          }
        },
        "required": [
          "AccountId",
          "TransactionId",
          "Amount",
          "CreditDebitIndicator",
          "Status",
          "BookingDate",
          "TransactionInformation"
        ]
      },
      "ObTransactions": {
        "type": "object",
        "properties": {
          "Data": {
            "type": "object",
            "properties": {
              "Transaction": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ObTransaction"
                },
                "maxItems": 500
              }
            },
            "required": [
              "Transaction"
            ]
          },
          "Meta": {
            "type": "object",
            "properties": {
              "TotalRecords": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "Offset": {
                "type": "integer",
                "format": "int64",
                "minimum": 0
              },
              "Limit": {
                "type": "integer",
                "format": "int64",
                "minimum": 1,
                "maximum": 500
              }
            },
            "required": [
              "TotalRecords",
              "Offset",
              "Limit"
            ]
          }
        },
        "required": [
          "Data",
          "Meta"
        ],
        "description": "Reversed/nonfinancial rows excluded. The core has booking dates: datetime filters are converted to their supplied-offset LocalDate and applied inclusively; this endpoint does not promise sub-day filtering."
      },
      "OutgoingException": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/TransferState"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN"
            ]
          },
          "client_reference": {
            "type": "string"
          },
          "owner": {
            "type": "string"
          },
          "deadline": {
            "$ref": "#/components/schemas/DbTimestamp"
          },
          "exception_code": {
            "type": "string"
          }
        }
      },
      "PartnerRegistration": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{3,64}$"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "redirectUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 512
            },
            "minItems": 1,
            "maxItems": 10
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Scope"
            },
            "minItems": 1,
            "uniqueItems": true
          }
        },
        "required": [
          "clientId",
          "name",
          "redirectUris",
          "scopes"
        ],
        "description": "Exact redirect allowlist; HTTPS or loopback HTTP only, without fragment/userinfo. Grants cannot exceed these registered read scopes."
      },
      "PartnerSecret": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string"
          },
          "clientSecret": {
            "type": "string",
            "writeOnly": false,
            "description": "One-time raw secret. Store securely; the server retains only its hash."
          }
        },
        "required": [
          "clientId",
          "clientSecret"
        ]
      },
      "PartnerState": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "SUSPENDED"
            ]
          }
        },
        "required": [
          "status"
        ]
      },
      "Payee": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "routeId": {
            "type": "string",
            "format": "uuid"
          },
          "accountNumber": {
            "type": "string"
          },
          "accountName": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "UNKNOWN",
              "CONFIRMED",
              "REJECTED"
            ]
          },
          "messageId": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/InstantEpochSeconds"
          }
        },
        "required": [
          "id",
          "routeId",
          "accountNumber",
          "status",
          "messageId",
          "expiresAt"
        ]
      },
      "PayeeInput": {
        "type": "object",
        "properties": {
          "routeId": {
            "type": "string",
            "format": "uuid"
          },
          "accountNumber": {
            "type": "string",
            "pattern": "^[0-9]{10}$"
          }
        },
        "required": [
          "routeId",
          "accountNumber"
        ]
      },
      "ProcessResult": {
        "type": "object",
        "properties": {
          "inboxProcessed": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "outboxProcessed": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "alertsAccepted": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        },
        "required": [
          "inboxProcessed",
          "outboxProcessed",
          "alertsAccepted"
        ]
      },
      "Receipt": {
        "type": "object",
        "properties": {
          "messageId": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "status": {
            "type": "string"
          },
          "duplicate": {
            "type": "boolean"
          }
        },
        "required": [
          "messageId",
          "status",
          "duplicate"
        ],
        "description": "Durable inbox receipt only. Does not assert customer credit or settlement."
      },
      "Revoked": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "REVOKED"
            ]
          }
        },
        "required": [
          "status"
        ]
      },
      "Route": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string"
          },
          "institutionCode": {
            "type": "string"
          },
          "peerCode": {
            "type": "string"
          },
          "clearingSavingsId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "settlementGlId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "receivableGlId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "paymentTypeId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "maxTransfer": {
            "$ref": "#/components/schemas/Amount"
          },
          "dailyLimit": {
            "$ref": "#/components/schemas/Amount"
          },
          "makerId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "ACTIVE"
            ]
          }
        },
        "required": [
          "id",
          "code",
          "status"
        ]
      },
      "RouteInput": {
        "type": "object",
        "properties": {
          "routeCode": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "institutionCode": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          },
          "peerCode": {
            "type": "string",
            "pattern": "^[0-9]{6}$"
          },
          "clearingSavingsId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "settlementGlId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "receivableGlId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "paymentTypeId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "maxTransfer": {
            "$ref": "#/components/schemas/AmountInput"
          },
          "dailyLimit": {
            "$ref": "#/components/schemas/AmountInput"
          },
          "authorities": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthorityInput"
            },
            "minItems": 3
          }
        },
        "required": [
          "routeCode",
          "institutionCode",
          "peerCode",
          "clearingSavingsId",
          "settlementGlId",
          "receivableGlId",
          "paymentTypeId",
          "maxTransfer",
          "dailyLimit",
          "authorities"
        ]
      },
      "Scope": {
        "type": "string",
        "enum": [
          "accounts:read",
          "balances:read",
          "transactions:read"
        ]
      },
      "SettlementInput": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Exact signed booked-statement message identity already persisted as matching settlement evidence."
          },
          "amount": {
            "$ref": "#/components/schemas/AmountInput"
          }
        },
        "required": [
          "reference",
          "amount"
        ]
      },
      "TokenPair": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string"
          },
          "refresh_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "maximum": 900
          },
          "scope": {
            "type": "string"
          }
        },
        "required": [
          "access_token",
          "refresh_token",
          "token_type",
          "expires_in",
          "scope"
        ],
        "description": "Access lasts at most 15 minutes; refresh lasts at most 30 days; both capped by consent expiry. Refresh rotates. Replaying a consumed refresh revokes the whole family. Consent revocation, partner suspension and secret rotation invalidate appropriate access immediately."
      },
      "Transfer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "routeId": {
            "type": "string",
            "format": "uuid"
          },
          "savingsId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "payeeId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN"
            ]
          },
          "reference": {
            "type": "string"
          },
          "narration": {
            "type": "string"
          },
          "makerId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "approverId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "status": {
            "$ref": "#/components/schemas/TransferState"
          },
          "fundingTransferId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "fundingTransactionId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "settlementTransactionId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "messageId": {
            "type": "string",
            "pattern": "^[0-9]{35}$"
          },
          "accountingEventId": {
            "type": "string"
          },
          "exceptionCode": {
            "type": "string"
          },
          "owner": {
            "type": "string"
          },
          "deadline": {
            "$ref": "#/components/schemas/InstantEpochSeconds"
          }
        },
        "required": [
          "id",
          "routeId",
          "savingsId",
          "payeeId",
          "amount",
          "currency",
          "reference",
          "makerId",
          "status",
          "messageId",
          "owner",
          "deadline"
        ],
        "description": "Nullable/unset record fields may be omitted. Receipt is SUBMITTED, not completion. UNKNOWN retains original funding; only signed authoritative failure permits a linked compensating credit. REVERSAL_PENDING retains the rejection while compensation needs repair/retry."
      },
      "TransferInput": {
        "type": "object",
        "properties": {
          "routeId": {
            "type": "string",
            "format": "uuid"
          },
          "savingsId": {
            "type": "integer",
            "format": "int64",
            "minimum": 1
          },
          "payeeId": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "$ref": "#/components/schemas/AmountInput"
          },
          "currency": {
            "type": "string",
            "enum": [
              "NGN"
            ]
          },
          "reference": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "narration": {
            "type": "string",
            "maxLength": 100
          }
        },
        "required": [
          "routeId",
          "savingsId",
          "payeeId",
          "amount",
          "currency",
          "reference"
        ],
        "description": "Reference is immutable within a route. Replay by the same maker with the same payload returns the original Transfer; changed payload or maker conflicts. Creation does not fund."
      },
      "TransferState": {
        "type": "string",
        "enum": [
          "PENDING_APPROVAL",
          "DISPATCH_PENDING",
          "SUBMITTED",
          "UNKNOWN",
          "COMPLETED",
          "REVERSAL_PENDING",
          "REVERSED",
          "SETTLED",
          "RECONCILED",
          "EXCEPTION"
        ]
      }
    }
  }
}
