Why this matters
For product and operations teams, modeling each balance as its own account means every segregation rule — a blocked outflow, a benefit-only spend, a promotional balance with an expiry — is enforced by the Ledger, not buried in application logic. Each account carries its own statement and reconciliation trail. For engineering teams, the customer’s external addresses (core banking numbers, payment-rail identifiers) resolve to a specific account before a transaction is posted. There’s no guessing which balance an incoming event belongs to, and no separate balance store to keep in sync with the Ledger.
The reference architecture
The model is one owner, N accounts, N external identifiers:
- One owner — the customer, identified by a document (CPF, CNPJ, tax ID). The owner represents who holds the relationship. It does not carry a balance and does not decide transaction routing.
- N accounts — each account is a self-contained accounting position, with its own balance, ledger, statement, and rules.
- N external identifiers — the addresses other systems (a core banking platform, a payment rail) use to reach a specific account. Each identifier resolves to exactly one account.
Reference architecture: one customer, many accounts.
Each external identifier is not just a nickname for one shared balance. When balance, ledger, statement, or operational rule differs by destination, each identifier must point to a distinct account.
How Midaz maps the model
Every part of this architecture maps to a native Midaz entity — you don’t need to build a separate balance store or invent an accounting layer.
Prerequisites
This example assumes a running Midaz environment with the following in place:
Values in Midaz are represented in the smallest unit of the currency. For BRL,
15000 means R$ 150.00 (centavos).Building three accounts for one customer
The customer with document
12345678900 needs three accounts: a main account for ordinary movement, a benefit account governed by product rules, and a blocked account that accepts inflows but restricts outflows.
1
Enable Account Type validation
Turn on validation so every account must declare a registered type. This is what lets the Ledger enforce each account’s nature.Settings take effect immediately — no redeployment needed.
2
Register the Account Types
Create one Account Type per balance nature. The Repeat for
keyValue is what each account’s type field must match.benefit_account (“Movement governed by product rules”) and restricted_account — the blocked account, where inflows are allowed but outflows are conditioned or blocked.3
Create the three accounts
Each account is linked to the Create the benefit account with
BRL asset, declares its type, and carries an alias (its in-Ledger address) and an entityId (its identifier in your external system).alias @cust_12345678900_benefit, entityId 0001/88888-2, type benefit_account; and the blocked account with alias @cust_12345678900_blocked, entityId 0002/77777-0, type restricted_account.4
Register the customer as a Holder
Create one Holder for the customer. The same Holder will own all three accounts, keeping identity centralized.Save the returned
CRM runs as a separate service, and every request requires the
X-Organization-Id header. See Getting started with CRM for service setup and the full schema.holderId — you’ll use it in the next step.5
Link each account to the Holder
Create an Alias Account per ledger account to attach banking and regulatory context. This is what powers CRM-driven features and keeps customer-facing details separate from the Ledger.Notice how the main account’s
entityId (0001/12345-1) decomposes into the branch (0001) and account (12345) you record here — that’s the external address resolving to one specific account. Repeat for the benefit and blocked accounts, pointing accountId at each one.The transaction boundary
When an external event arrives, the resolution happens before Midaz is called. Keeping each layer in its lane is what preserves accounting clarity.
1
External event
A transaction, query, or settlement arrives carrying an external identifier.
2
Middleware resolves the identifier
The middleware looks up the external identifier and resolves it to the correct Midaz account alias, applying status validation (active, blocked, closed).
3
Midaz posts to the right account
Midaz records the entry against the resolved account, preserving balance and ledger. Statement and reconciliation stay separated by account.
What this unlocks
- Real segregation — each balance has its own ledger and statement, so a blocked balance can never be spent through the main account by accident.
- Unambiguous routing — every external event has a single, well-defined destination account.
- Centralized identity — one Holder owns many accounts; identity and contact data live in one place while balances stay separate.
- Native, not bolted-on — accounts, types, aliases, and
entityIdare platform primitives, so there’s no parallel balance store to reconcile against the Ledger.
What you need to get started
Next steps
Accounts
The core financial unit — aliases,
entityId, and external accounts.Account Types
Classify accounts and enforce their nature with route validation.
Portfolios
Group a customer’s accounts to view the total relationship.
CRM: Holders & Alias Accounts
Centralize identity and attach banking and regulatory context.

