Program Architecture

The platform hierarchy

📘

This page is the map for the entire platform's documentation set. The hierarchy, terminology, and ID scoping defined here are referenced from every Concept, Guide, and Recipe that follows — if you read nothing else before integrating, read this.

The platform operates as a single, shared infrastructure layer on top of which many independent Tenants each operate one or more Card Program(s). Every Cardholder, Card, Account, and Wallet exists within the scope of exactly one Program, and every Program exists within the scope of exactly one Tenant.

flowchart TD
    PLAT["Axys Platform"]
    TEN["Tenant<br/>(licensed entity — certificate field O)"]
    PROGA["Program A<br/>(certificate field OU)"]
    PROGB["Program B<br/>(white-label / co-brand)"]
    CH["Cardholder"]
    CHACC["Cardholder Account<br/>(internal ledger)"]
    WAL["Wallet<br/>(fiat sub-ledger)"]
    CARD["Card — virtual or physical"]
    CACC["Card Account<br/>ledgerBalance / availableBalance"]
    DA["Deposit Addresses<br/>(80+ chains)"]
    DESIGNS["Card Designs<br/>(slots 0–5)"]
    TXN["Transactions & fees"]
    OTP["3-DS OTP subscription"]
    EXTBNK["External bank account"]
    EXTWLT["External blockchain wallet"]
 
   	J(( )):::invisible
    style EXTBNK fill:lightgray
		style EXTWLT fill:lightgray

    PLAT --> TEN
    TEN -->|creates and manages| PROGA
    TEN -->|creates and manages| PROGB
    PROGA --> CH
    PROGA --> DESIGNS
    CH --> CHACC
    CH --> CARD
    CH -.->|may use| WAL
    CARD --> CACC
    CARD --> DA
    CACC -.-> TXN
    CARD --> OTP
    WAL -.->|transfers to| CACC
    DESIGNS -.->|allocated to| CARD
    EXTBNK -.->|transfers to| CHACC
    CHACC -.->|STP| J
    J -.-> CACC
		J -.-> WAL
		EXTWLT -.->|transfers to| DA
		DA -.->|STP| CACC

The sections below define each layer in turn, working down the hierarchy. Solid arrows in the diagram represent ownership/containment; dashed arrows represent optional or transactional relationships.


The platform

The platform unifies and systemizes the underlying card issuing, management, and processing infrastructure: it is the connector between the Network Member, Issuer, Issuer Bank, Issuer Processor and Program Manager functions, the Card Networks (sometimes called Schemes), the digital-asset custody and settlement infrastructure, the fiat banking rails, and the compliance framework. It isn't something you address directly via the API — it's the context in which every Tenant, Program, and Cardholder exists.

The platform is positioned as a turnkey aggregation, automation, and orchestration layer for card issuing — unifying the roles and functions that a traditional card program would otherwise need to assemble and operate separately, with built-in network, payment, and digital-asset rails. See Stakeholders in a Card Program for how these industry roles map onto the platform hierarchy described on this page.


Tenants

A Tenant is a licensed or regulated entity (for example, the e-money institution or bank that holds the platform account) that has been onboarded onto the platform. A Tenant:

  • holds its own mTLS client certificate, whose O (Organization) field identifies the Tenant's legal entity name — see Mutual TLS, Certificates & CSRs
  • holds its own Tenant-level API Key credentials, used for Account Management operations such as creating and configuring Programs, managing API Users, and configuring network security
  • can create and operate one or more Programs — its own branded program, white-label programs on behalf of clients, or co-branded programs
🚧

A Tenant cannot directly create Cardholders, issue Cards, manage Wallets, or perform any other Cardholder-facing operation. Every such operation exists only within the scope of a Program and its Program-level credentials. If you're holding Tenant-level credentials and trying to call an endpoint like POST /cardholders, you're at the wrong layer — see API Users, Tenants & Programs.

A single Tenant operating multiple Programs is the normal white-label hotel pattern: one regulated entity, several distinct card programs (each potentially with its own brand, currency, card designs, and even its own end-customer base), all running on the same underlying infrastructure.

In industry terms, in the context of the platform, the Tenant is the Issuer — the regulated party whose Programs issue Cards to Cardholders. The platform itself is the Issuer Processor layer, aggregating and orchestrating the underlying issuing, processing, and program management roles on the Tenant's behalf. See Stakeholders in a Card Program for the full industry-role mapping, including Issuer Banks and Network Members.


Programs

A Program is a specific, configured card program operated under a Tenant. Each Program has its own:

  • base currency (one of USD, CAD, GBP, or EUR — see the currency enum in Status & Enum Glossary)
  • transactionLimit ceiling, which is inherited by every Cardholder and Card in the Program unless overridden at a lower level (and can never be overridden upward)
  • set of Card Designs (six slots — see Card Programs & Card Designs)
  • compliance configuration (standard Didit-hosted KYC, or SumSub token-based KYC via sumsubEnabled)

A Program's mTLS client certificate carries one or more OU (Organizational Unit) entries identifying the Program's trading or brand name(s). Critically, the documented API (everything in the Reference section) operates entirely at the Program levelprogramId is never passed as a parameter because it's implicit in the Program-level API Key used to authenticate the request. Every Cardholder, Card, Wallet, transaction, and fee you can see through the API belongs to exactly one Program: the one whose credentials you're using.

Each Program is designed and operated by a Program Manager — typically the Tenant itself, but in white-label or co-brand arrangements, potentially a third party operating alone or in conjunction with the Tenant. See Stakeholders in a Card Program.


Cardholders

A Cardholder is always a natural person. KYC, the compliance lifecycle, and ultimate liability for the Cards issued under an Account all attach to an individual — never directly to a corporate entity.

Programs are free to serve corporate clients, but they do so through a Cardholder: the natural person who represents the business and is responsible for the Cards issued under their Account — typically a director, manager, partner, shareholder, entrepreneur, or sole trader. A corporate entity itself never appears as a Cardholder record; see "Corporate use cases" below for how Wallets fit into this pattern.

Cardholders move through a compliance-driven lifecycle (Draft → Under Review → Approved, with decline and error states) — see Cardholders & the Compliance Lifecycle for the full status model and onboarding flow.

On creation (POST /cardholders), Axys automatically provisions the Cardholder's Account — see below.


Accounts, Wallets & Deposit Addresses

These three terms sound similar but describe different things, and the distinction matters for how money moves through the platform. This page gives the short version; for the full treatment — including how balances move between them — see Accounts, Wallets & Deposit Addresses.

TermWhat it isExposed as a REST resource?
AccountAn internal ledger. Every Cardholder gets one on creation (the Cardholder Account), and every Card also gets its own (the Card Account — the latter is what ledgerBalance and availableBalance on GET /cards/{cardId}/balance represent).No — internal only
WalletAn explicitly created (POST /wallets), named, fiat-only sub-ledger. A common pattern is a corporate client's company-wide funds pool, from which individual Cardholders' Cards (e.g. each director's expense card) are funded by transfer. Funds can be transferred between Wallets and Cards, and between Wallets.Yes — /wallets
Deposit AddressA blockchain address automatically generated for a Card on issuance, one per supported chain (80+ chains). Purely a deposit funnel for straight-through processing into that corresponding Card Account — it holds no balance of its own.Yes — as the cryptoAddresses field on a Card object
📘

If you only remember one rule from this table: Wallet always means fiat, and a blockchain address attached to a Card is a Deposit Address, never a wallet (despite the blockchain industry's adoption of the term "wallet").

Corporate use cases

Because a Cardholder is always a natural person, a Program serving a corporate client typically combines a Wallet (representing the corporate entity's pooled funds) with one or more Cardholders (the directors, employees, or other individuals authorized to spend on the company's behalf). A typical pattern:

  1. The Program creates a Wallet for the corporate client and funds it (via bank transfer or digital-asset deposit).
  2. Each authorized individual is onboarded as their own Cardholder, with their own KYC and their own Card(s) — e.g. a "director's expense card."
  3. Funds are transferred from the corporate Wallet to each Cardholder's Card Account as needed (POST /wallets/transfer).
📘

The platform has no means to onboard or conduct KYB on a corporate entity, which is never a Cardholder on the platform — liability for each Card sits with the natural person who holds it.


Cards

A Card — virtual or physical — is issued to a Cardholder and has:

  • its own Card Account (internal ledger — ledgerBalance and availableBalance)
  • its own set of auto-generated Deposit Addresses for digital-asset funding
  • a cardType (0–5) selecting which of the Program's six Card Design slots applies
  • an optional Card-level transactionLimit, which inherits the Cardholder's limit (itself capped by the Program's limit) if not set

See Cards: Virtual vs. Physical for the full lifecycle, status model, and the distinction between a card's approval status and its operational status.


Card Designs

Each Program has six Card Design slots (numbered 0–5). Slot 0 must be populated with the Program's default design; slots 1–5 are optional. Each design carries its own approval status (Pending, Approved, or Rejected), and cardType at issuance selects which design slot a physical card uses. See Card Programs & Card Designs.


Transactions, fees & 3-D Secure

Every Card Account accumulates transactions and fees as money moves in and out — see Transactions & Fees for the full model, including how the platform's Settlement and fee structure differ from traditional card-program economics. Certain Cards can also subscribe to 3-D Secure OTP delivery for Card Not Present authentication — see 3-D Secure.


Credential scoping

Two distinct credential scopes exist, corresponding to the Tenant and Program layers:

ScopeCredentialsmTLS certificate fieldCan operate on
TenantTenant-level API Key + tenant mTLS certificateO = Tenant's legal entity nameAccount Management: create/configure Programs, manage Card Designs, manage API Users, configure IP allowlist and certificates — see API Users, Tenants & Programs
ProgramProgram-level API Key + program mTLS certificateOU = Program's trading/brand name(s)Everything in the Reference section: Cardholders, Cards, Wallets, transactions, fees, 3-DS OTP

Both scopes require a valid mTLS client certificate and an X-API-Key header on every request — see Authentication & Environments.


ID reference

IDTypeWhere it comes fromUsed as
cardholderIdintegerReturned by POST /cardholdersPath parameter for all Cardholder-scoped endpoints
cardIdintegerReturned by POST /cards/virtual/{cardholderId} or POST /cards/assign/{cardholderId}Path parameter for all Card-scoped endpoints
walletIdintegerReturned by POST /walletsPath parameter for Wallet-scoped endpoints, and as sourceWalletId/destinationId in transfers
transIdstringReturned in transaction list responsesPath parameter for GET /wallets/transactions/{transactionId}
Program identity(implicit)Determined by the Program-level API Key used to authenticateNever passed explicitly — see "Programs" above
Tenant identity(implicit)Determined by the Tenant-level API Key / certificate O fieldUsed only in Account Management

How this maps to your mTLS certificate

If you've been through onboarding, you already hold at least one client certificate. The certificate's Subject fields tell you which layer it operates at:

  • O (Organization) — your Tenant's legal entity name
  • OU (Organizational Unit) — one or more trading/brand names, corresponding to your Program(s)
  • CN/SAN — the explicit domain(s)/IP(s) you'll call the API from (no wildcards)

For the full certificate issuance and rotation process, see Mutual TLS, Certificates & CSRs.


Multi-Program and white-label scenarios

Because every documented endpoint is scoped implicitly to a Program via its API Key, running multiple Programs under one Tenant is simply a matter of holding multiple sets of program-level credentials — one per Program. Common patterns:

  • Single brand, single Program — the simplest case: one Tenant, one Program, one set of credentials.
  • Single Tenant, multiple owned Programs — for example, separate consumer and corporate card programs, each with its own currency, limits, and card designs.
  • White-label / co-brand — a Tenant operates a Program on behalf of (or jointly branded with) a separate client business. The client's brand appears in the Program's OU and Card Designs, but the Tenant remains the regulated entity and retains Account Management control over that Program.

In every case, Cardholders, Cards, Wallets, transactions, and fees from one Program are completely invisible to another Program's credentials — there is no cross-Program query capability in the documented API.


The full data model

The diagram below shows every object referenced in this documentation, including Tenant-level objects that are part of the Account Management layer. For a field-by-field reference of each object, see Data Model & Object Reference.

erDiagram
    TENANT ||--o{ PROGRAM : "creates and manages"
    TENANT ||--o{ API_USER : "manages"
    PROGRAM ||--o{ CARDHOLDER : "onboards"
    PROGRAM ||--o{ CARD_DESIGN : "configures (slots 0-5)"
    PROGRAM ||--o{ WALLET : "provisions"
    CARDHOLDER ||--|| CARDHOLDER_ACCOUNT : "has"
    CARDHOLDER ||--o{ CARD : "holds"
    CARD ||--|| CARD_ACCOUNT : "has"
    CARD ||--o{ DEPOSIT_ADDRESS : "generates per chain"
    CARD_ACCOUNT ||--o{ TRANSACTION : "records"
    CARD_ACCOUNT ||--o{ FEE : "incurs"
    CARD ||--o{ OTP_LISTENER : "may have"
    CARD_DESIGN ||--o{ CARD : "selected via cardType"
    WALLET ||--o{ WALLET_TRANSACTION : "records"
    WALLET }o--o{ CARD : "transfers to or from"

    TENANT {
        string legalEntityName "certificate field O"
        string tradingNames "certificate field OU values"
    }
    PROGRAM {
        int currency "0 USD, 1 CAD, 2 GBP, 3 EUR"
        int transactionLimit "ceiling for cardholders and cards"
    }
    CARDHOLDER {
        int cardholderId
        int status "0-7 compliance lifecycle"
        int transactionLimit
        int sumsubEnabled "0 or 1"
    }
    CARDHOLDER_ACCOUNT {
        string note "internal ledger, not a REST resource"
    }
    CARD {
        int cardId
        string type "physical or virtual"
        int cardStatus "0-2 approval status"
        int issuerCardStatus "0-4 lifecycle status"
        int transactionLimit
        int cardType "0-5, selects design slot"
    }
    CARD_ACCOUNT {
        int ledgerBalance
        int availableBalance
        int currency "0 USD, 1 CAD, 2 GBP, 3 EUR"
    }
    DEPOSIT_ADDRESS {
        string chain
        string address
    }
    WALLET {
        int walletId
        string name
        int walletCurrencyId
        boolean isActive
        boolean isDeleted
    }
    CARD_DESIGN {
        int slot "0-5, slot 0 mandatory"
        int status "0 pending, 1 approved, 2 rejected"
    }
    TRANSACTION {
        string transId
        string amount
        int transType
        int transStatus
    }
    FEE {
        string feeId
        int feeType
        int feeStatus
    }
    API_USER {
        string id "Account Management"
    }

What's next


Did this page help you?