How it works

One contract in front. An adapter behind it.

The banking app only ever speaks our canonical Banking API. Everything specific to your core lives in one adapter, chosen per tenant by configuration. Swap the adapter and the app, its tests and its security model stay the same.

Architecture

The app calls one canonical Banking API; an adapter per core translates each call. The web app calls the canonical Banking API at /api/v1. The API calls an adapter port, a Go interface. Three adapters implement it: simulator (built-in synthetic core for tests and demos), mambu-v2 (Mambu v2 API, used by the fictional Kesef Bank) and cdr-banking (Australian CDR Banking API, read-only, used by the fictional Riverbank). A fourth, dashed slot is your core's adapter. YOUR CUSTOMERS SEE ONE CONTRACT ADAPTER LAYER YOUR CORE Web banking app Vue, one build for every tenant apps/web Canonical Banking API /api/v1 · OpenAPI 3.1 /me /accounts /accounts/{id}/balance /…/{id}/transactions /loans /cards /transfers Adapter port (Go interface) picked by integration.adapterId simulator synthetic core, tests and demos mambu-v2 reads, transfers, loans cdr-banking read-only: no transfers, no cards your-core same interface, scoped with you In-process simulator fixtures for Alice and Bob Mambu v2 API Kesef Bank · Fictional bank CDR Banking API (AU) Riverbank · Fictional bank Your core Temenos, Finastra, FIS, Fiserv, other GET /capabilities → the app hides what the core can’t do The app calls one canonical Banking API; an adapter per core translates each call. The web app calls the canonical Banking API at /api/v1. The API calls an adapter port, a Go interface. Three adapters implement it: simulator (built-in synthetic core for tests and demos), mambu-v2 (Mambu v2 API, used by the fictional Kesef Bank) and cdr-banking (Australian CDR Banking API, read-only, used by the fictional Riverbank). A fourth, dashed slot is your core's adapter. Web banking app One Vue build for every tenant Canonical Banking API /api/v1 · OpenAPI 3.1 /me /accounts /balance /transactions /loans /cards Adapter port (Go interface) simulator → In-process synthetic core tests and demos mambu-v2 → Mambu v2 API Kesef Bank · Fictional bank cdr-banking (read-only) → CDR Banking API (AU) Riverbank · Fictional bank your-core → Your core, same interface GET /capabilities tells the app what to hide
The app never calls a core. Each tenant config names one adapter; the adapter declares its capabilities and the app renders only those.
  1. The app asks for a resource

    The browser calls GET /api/v1/accounts/{id}/balance. It never learns which core is behind the API, and it never holds a core credential.

  2. The API checks the session

    The customer comes from the session, not from the URL. Ask for someone else’s account and you get the same 404 as a missing one, plus a denial in the audit trail.

  3. The adapter translates

    It calls the core with credentials from a Kubernetes Secret, maps the answer to canonical models and keeps decimals as exact strings. Upstream calls time out at 5 seconds; reads retry twice.

  4. Writes are never blindly retried

    A transfer goes to the core once, with its operation reference as the idempotency key. If the answer is lost, the worker looks the transfer up by that reference instead of sending it again.

The mapping

Same resource, one canonical call, two different cores.

Canonical paths sit under /api/v1 and are scoped to the signed-in customer. The upstream calls are what each adapter makes today; each adapter release ships its own field-by-field mapping document.

The Mambu adapter is built and tested against a contract mock from Mambu’s published v2 API specification; validation against a live Mambu sandbox is in progress. The CDR adapter is tested against a CDR data-holder test double.

Canonical Banking API resources and the upstream calls each adapter makes
Resource App calls (canonical) mambu-v2 Kesef Bank Fictional bank cdr-banking Riverbank Fictional bank
Customer GET /me GET /clients/{clientId} GET /common/customer (x-v 1)
Accounts and balances GET /accountsGET /accounts/{accountId}GET /accounts/{accountId}/balance GET /deposits?accountHolderType=CLIENT&accountHolderId={clientId} GET /banking/accounts (x-v 2)GET /banking/accounts/balances (x-v 1)
Transactions GET /accounts/{accountId}/transactions GET /deposits/{id}/transactions GET /banking/accounts/{id}/transactions (x-v 1)
Loans GET /loansGET /loans/{loanId} GET /loans?accountHolderType=CLIENT&accountHolderId={clientId}GET /loans/{id}/schedule GET /banking/accounts/{id} (x-v 4) for lending products
Own-account transfer POST /transfer-quotesPOST /transfersGET /transfers/{operationId} POST /deposits/{fromId}/transfer-transactionsIdempotency-Key = operation referencePOST /deposits/transactions:search (status lookup by externalId) Not offered: CDR Banking is read-only
Cards GET /cardsPOST /cards/{cardId}/freezePOST /cards/{cardId}/unfreeze Not offered: Mambu v2 has no card freeze Not offered: no cards endpoint in CDR Banking
  • mambu-v2 Kesef Bank Fictional bank
  • cdr-banking Riverbank Fictional bank
  1. Customer

    App calls (canonical)
    GET /me
    mambu-v2 Mambu v2 adapter
    GET /clients/{clientId}
    cdr-banking CDR Banking adapter
    GET /common/customer (x-v 1)
  2. Accounts and balances

    App calls (canonical)
    GET /accountsGET /accounts/{accountId}GET /accounts/{accountId}/balance
    mambu-v2 Mambu v2 adapter
    GET /deposits?accountHolderType=CLIENT&accountHolderId={clientId}
    cdr-banking CDR Banking adapter
    GET /banking/accounts (x-v 2)GET /banking/accounts/balances (x-v 1)
  3. Transactions

    App calls (canonical)
    GET /accounts/{accountId}/transactions
    mambu-v2 Mambu v2 adapter
    GET /deposits/{id}/transactions
    cdr-banking CDR Banking adapter
    GET /banking/accounts/{id}/transactions (x-v 1)
  4. Loans

    App calls (canonical)
    GET /loansGET /loans/{loanId}
    mambu-v2 Mambu v2 adapter
    GET /loans?accountHolderType=CLIENT&accountHolderId={clientId}GET /loans/{id}/schedule
    cdr-banking CDR Banking adapter
    GET /banking/accounts/{id} (x-v 4) for lending products
  5. Own-account transfer

    App calls (canonical)
    POST /transfer-quotesPOST /transfersGET /transfers/{operationId}
    mambu-v2 Mambu v2 adapter
    POST /deposits/{fromId}/transfer-transactionsIdempotency-Key = operation referencePOST /deposits/transactions:search (status lookup by externalId)
    cdr-banking CDR Banking adapter
    Not offered: CDR Banking is read-only
  6. Cards

    App calls (canonical)
    GET /cardsPOST /cards/{cardId}/freezePOST /cards/{cardId}/unfreeze
    mambu-v2 Mambu v2 adapter
    Not offered: Mambu v2 has no card freeze
    cdr-banking CDR Banking adapter
    Not offered: no cards endpoint in CDR Banking

CDR paths are relative to the data holder’s /cds-au/v1 base. Mambu paths are relative to the tenant’s Mambu v2 API base and are checked against Mambu’s published v2 specification. Mambu and CDR are named to describe compatibility only; we are not a Mambu partner or a CDR accredited data recipient.

Capabilities

No greyed-out buttons.

Each adapter declares what its core can do. GET /capabilities passes that to the app, which leaves out any entry point the core can’t serve. The server still checks every action, so a hidden feature is also a refused one.

Kesef Bank Fictional bankmambu-v2
GET /api/v1/capabilities

{
  "enabledModules": [
    "customer", "accounts", "transactions",
    "payments", "loans"
  ],
  "transferCurrencies": ["AUD"]
}

Shows Move money and Transfer history.

Riverbank Fictional bankcdr-banking
GET /api/v1/capabilities

{
  "enabledModules": [
    "customer", "accounts", "transactions",
    "loans"
  ],
  "transferCurrencies": []
}

No transfer or card entry points at all. That’s a difference in the core, not a defect.

Responses abridged to the fields that differ.

Your core

Running something else?

An adapter is one Go package behind a fixed interface, with a contract-mock test suite. We scope a new one against your core’s API and your sandbox. The app doesn’t change.