Description
Accounts & Portfolios
The accounts module provides a unified interface for managing a customer's total financial portfolio, encompassing traditional fiat bank accounts and cryptocurrency assets.
Purpose and use
Accounts are the operational view of where customer money and crypto assets can be held, spent, frozen, reported, or reconciled. They sit between customer onboarding, ledger posting, transfer initiation, card or payment holds, and portfolio reporting.
Who uses this. Operations staff open and review accounts, support teams answer balance questions, finance teams reconcile account balances to subledgers, and product teams decide which account types are available to customers.
How it works. Each fiat account is backed by a customer subledger in the ledger module. Posted balance shows settled value-dated money; available balance reflects holds, pending movements, overdraft policy, and any money reserved for operations. Crypto balances are shown through the same portfolio view so users can scan all customer holdings together.
What users do. Users create an account during onboarding or product activation, review balances by currency, group accounts for portfolio totals, search by customer, IBAN, currency, or subledger code, and deactivate accounts that should no longer be used for new activity.
Outcomes and side effects. Account opening creates or links the relevant ledger container, may issue an IBAN or wallet address, and makes the account eligible for transfers, statements, fees, and audit review. Deactivation removes the account from active balance totals without deleting historical postings.
Related manuals: Ledgers & Balance Management (includes customer subledgers and purpose accounts), Transfers, Customers.
Object Relationships
CoreBanq uses a "Unified Account" model where fiat and crypto entities are normalized into a consistent structure, each backed by the CoreBanq Ledger system.
Key Components
- Account: The high-level entity representing a customer's wallet or IBAN account. It holds metadata (description, status, tags).
- Subledger: The actual accounting container where balances are tracked and entries are posted. Every Account has exactly one Subledger.
- Customer: The legal entity (Individual or Business) that owns the accounts. When account responses hydrate the nested
customerobject, it includes nullablecounterparty_idfor the customer's mirror-linked counterparty.
Balance Management
Following the V2/V3 Unified API standards, accounts now expose detailed balance states derived directly from their underlying subledgers.
Available, Posted, and Pending Balance
| Balance Type | Meaning |
|---|---|
| Available | The "spendable" amount. It accounts for administrative holds, pending card authorizations, and uncleared funds. |
| Posted | The settled, legally recognized balance. This only changes when a transaction is fully cleared and value-dated. |
| Pending | Funds that are in flight — booked but not yet cleared or released (for example, a hold awaiting settlement). |
Every balance in an account or portfolio response is expressed as a self-describing amount: a whole-number value in the currency's smallest unit (cents, satoshi, etc.), paired with the currency code and the number of decimal places it represents. This removes any ambiguity about rounding or decimal placement when displaying or reconciling amounts across fiat and crypto. When an account is backed by a Subledger, its posted, available, and pending balances are included the same way.
API Reference
Unified Accounts (V2 / V3)
The Unified API is the recommended way to interact with portfolios. It supports complex filtering and provides a consistent schema regardless of the asset type.
GET /v3/accounts
Retrieves all accounts (Fiat + Crypto). Use search.is_crypto=true/false to filter.
What a Unified Account Includes
Each record combines the account's identity (owning customer, currency, current status) with its balance and its business type (fiat or crypto). Fiat accounts additionally carry their IBAN and bank details; crypto accounts carry their network and wallet information. When the account has a Subledger, the record also includes the subledger's posted, available, and pending balances, each expressed as the same self-describing amount used everywhere else in the module.
Portfolio Balances
GET /v2/accounts/balance
Calculates total portfolio value in a target currency (e.g., EUR or USD).
Balance endpoints use active accounts only. Accounts deactivated by delete operations are excluded from portfolio totals and currency-specific totals.
Parameters:
customer_id: The customer whose portfolio is being valued (Required).currency: The target currency for conversion (Required).details: Set totrueto receive the full breakdown of fiat accounts and crypto assets.
The customer must exist and be readable by the caller: a customer_id that does not match
any customer is rejected with 404 (customers_m.not_found), and a customer the caller
has no read permission on is rejected with 403 (common.forbidden) — whether or not
that customer exists, so the endpoint cannot be used to probe customer IDs. A readable
customer with no active accounts is a valid portfolio and returns 200 with zero
balances. The same order and codes apply to the legacy POST /v1/accounts/balance and
POST /v1/accounts/balance/currency endpoints.
Grouped Portfolio Totals
GET /v3/accounts/balance
Returns the same portfolio total, but split into Total, Fiat, and Crypto groups, each broken down into Posted, Available, and Pending balances. Use this when a dashboard or reconciliation view needs to separate fiat exposure from crypto exposure at a glance, rather than working from the single blended total returned by the grouped-by-currency endpoint above.
The same customer validation applies as on the v2 endpoint above: 404
(customers_m.not_found) for a customer_id that matches no customer, 403
(common.forbidden) when the caller has no read permission on the customer record.
Parameters:
customer_id: The customer whose portfolio is being valued (Required).currency: The target currency for conversion (Required).
Usage Guidelines
Creating Accounts
When creating a fiat account, the system automatically:
- Creates the Account metadata.
- Resolves the correct Control Ledger from the customer type + account currency (see below).
- Provisions a unique Subledger under that control ledger (e.g., Customer Deposits (Available)).
- Generates a virtual IBAN if configured for the target currency.
How the control ledger is chosen
The target control ledger is not fixed per account type in code — it is resolved from the pair (customer type, account currency). Each leaf control ledger declares which customer types it serves through its own Customer Types list (Corebanq Configurator → Ledger Browser → open the control ledger → "Customer Types"), and a given type + currency pair maps to exactly one control ledger. See Routing by customer type in the Ledgers manual for how to set this.
If no control ledger claims the account's customer type + currency, account creation fails and no account or subledger is created — assign that type + currency on the correct leaf control ledger's Customer Types list to resolve it (see the Error Codes table below).
Search and Filtering
The Unified API supports advanced query syntax:
search.balance.gte=1000: Minimum balance filter.search.subledger_code=2211-*: Search by accounting code (2211 is the Type A / CHF customer-deposit control leaf in the standard chart).stack=currency: Group results by currency for a summarized dashboard view.
Deleting Accounts
DELETE /v1/accounts/{id} deactivates the account by setting active=false; it does not physically remove the accounts.accounts row. Lookup by IBAN, customer account balance sources, and currency-specific balance sources return active accounts only, so a deactivated account is treated as not found or excluded from totals. Reading a single account by ID (GET /v1/accounts/{id}, GET /v2/accounts/{id}, GET /v3/accounts/{id} — v3 is routed to the same unified handler as v2) returns a deactivated account only to callers holding the Internal role; everyone else gets 404.
Account lists (GET /v1/accounts, GET /v2/accounts, GET /v3/accounts) show deactivated accounts only to callers holding the Internal role — GET /v3/accounts is the same unified handler as v2. Everyone else sees active accounts only, and the reported total and total_unfiltered counts exclude the hidden rows.
Purpose-segregated subledgers
Beyond the always-present available deposit subledger every account is opened with, an operator can open additional purpose-segregated subledgers so a customer's money is held in the correct liability control family for its state. These are empty buckets: opening one posts no entries and moves no money — money only moves later by journal postings between subledgers. The purposes available today:
- Pending Settlement (Incoming / Outgoing) — funds in transit to or from an account, awaiting settlement. Account-level: opened against a specific account.
- Compliance Hold (Incoming / Outgoing) — funds held on a specific payment pending compliance review. Account-level.
- Accruals for Fees — the running claim for fees a customer owes. Customer-level: one bucket per customer + currency, shared by all of that customer's accounts in that currency.
(Regulatory freeze is planned but not yet available — its account-vs-customer scope is still being decided.)
Who uses this. Operations and compliance staff, when a payment needs to be parked in a settlement or hold state, or when fees begin accruing against a customer.
Open a purpose subledger for an account
From the account, choose the purpose to open. The system finds the control ledger that serves the account's customer type + currency for that purpose and provisions the subledger beneath it. The operation is idempotent: opening a purpose that is already open simply returns the existing subledger rather than creating a duplicate or failing. Opening a purpose subledger requires create permission on the account. If the chosen purpose is the customer-level Accruals for Fees, create permission on the owning customer is also required, because that bucket is shared across all the customer's accounts.
If no control ledger is configured for the account's customer type + currency for that purpose, the open fails with a routing-configuration error — assign that type + currency on the correct control ledger first (see How the control ledger is chosen).
Review an account's subledger catalog
An operator can pull the full catalog for an account: every purpose, whether it is routable
(a control ledger is configured for the account's customer type + currency) and whether it is
already open. This drives the account's segregation menu — purposes with no configured control
ledger show as unavailable. Reviewing the catalog requires read permission on the account.
For a customer-level Accruals for Fees bucket, open remains visible but subledger (including
balances) is null unless the operator also has read permission on the owning customer. The
account-scoped response includes the shared customer bucket for each account queried; clients that
combine catalogs across accounts should deduplicate it by customer + currency + purpose.
Open a customer-level subledger before any account exists
The customer-level Accruals for Fees bucket can also be opened directly against a customer, in a chosen currency, without an account — useful when fees begin accruing before the customer holds an account in that currency. Every one of the customer's accounts in that currency then shares the same bucket. This open is also idempotent and requires create permission on the customer. Account-level purposes (Pending Settlement, Compliance Hold) cannot be opened this way — they are tied to a specific account.
Discover which purposes a customer can open
Before an account exists, an operator (for example, in the open-account flow) can ask which purposes are routable for a customer + currency — i.e. which purposes have a control ledger configured for the customer's type and the chosen currency — so the menu offers only purposes that will actually open. This is a read-only lookup requiring read permission on the customer.
Error Codes
| Code | Description |
|---|---|
common.record_not_found | The requested account or customer does not exist. |
customers_m.not_found | The customer_id sent to a portfolio balance endpoint does not match any customer. |
common.forbidden | The caller has no read permission on the requested customer record. |
accounts_m.invalid_iban | The provided IBAN failed checksum validation. |
accounts.invalid_account_status | The provided account status is not one of active, inactive, pending, restricted, closed. |
ledgers.ledger_not_found | The underlying subledger for this account is missing or inaccessible. |
ledgers.invalid_ledger_configuration | No control ledger is configured to route the account's customer type + currency. Set that type + currency on the correct leaf control ledger's Customer Types list (see How the control ledger is chosen). Also raised when opening a purpose subledger for a purpose that has no control ledger configured for that customer type + currency. |
ledgers.failed_to_fetch_ledgers | The control-ledger lookup itself failed to read — a database error, not a missing route. |
ledgers.subledger_purpose_invalid | The requested subledger purpose is unknown, empty, the auto-created available purpose, or a legacy non-openable purpose. |
ledgers.subledger_purpose_anchor_mismatch | An account-level purpose was requested on the customer-level open path (or vice versa) — open it from the correct place. |
fx_m.exchange_rate_not_found | Could not calculate total balance due to missing FX rates. |