Skip to main content
This guide walks you through implementing accounting in Midaz from start to finish. It assumes you are a developer who wants enough accounting context to model a real product correctly — not a full accounting textbook. By the end you will understand how the primitives fit together and how to wire up a complete Pix payment with proper double-entry postings. For the conceptual overview and links to each reference page, see Accounting.

1. Double-entry accounting fundamentals


Midaz is built on strict double-entry bookkeeping. The rules are simple but non-negotiable:
  • Every transaction must have at least one debit and one credit.
  • Total debits must equal total credits.
  • Every movement impacts the ledger in a balanced way.
This guarantees no drift in balances, precise audit trails, and regulation-ready financial statements. In practice you rarely “do” double-entry by hand in Midaz — you model your accounts and routing once, and the engine enforces balance on every transaction.
Think in terms of where value comes from (the debit side / source) and where it goes (the credit side / destination). Every Midaz operation lands on one side of that equation.

2. Chart of Accounts in Midaz


In traditional accounting, the Chart of Accounts (CoA) defines account categories (Assets, Liabilities, Equity, Income, Expenses), their hierarchy, and how movements are classified.
Midaz does not have a dedicated Chart of Accounts API. The CoA is not a resource you create or retrieve — it is the result of how you combine assets, accounts, segments, portfolios, and account types. The code field on Accounting Entries (e.g. 1.1.1.001) is where traditional account numbering appears in practice, annotating each posting with its classification.
Midaz lets you mirror or adapt a CoA digitally using a small set of primitives:
  • Assets define what is moved — currencies (BRL, USD), points/miles, crypto tokens, or internal units of value — along with decimal precision and regulatory metadata.
  • Accounts are balance containers. Each belongs to a portfolio, a segment, an account type, and an asset code, and is identified by an alias (e.g. @external/BRL) to make routing intuitive.
  • Segments categorize and isolate accounts (customer vs. internal funds, business units, multi-tenant separation).
  • Portfolios group accounts that share a purpose or belong to the same entity.
Mapping a CoA in Midaz means deciding which balances you need, classifying them with Account Types, organizing them with segments and portfolios on a ledger, and assigning code values on your Accounting Entries to reflect your account numbering scheme.
1

Map your financial model

List the balances you need: customer balances, internal accounts, reserve/settlement accounts, fee and revenue accounts. This is your CoA blueprint.
2

Define account types

Create an Account Type per conceptual category — e.g. CASH, SETTLEMENT, FEE_REVENUE, FEE_EXPENSE, TREASURY.
3

Create segments and portfolios

Segments separate business domains (CUSTOMER_FUNDS); portfolios manage ownership and grouping (customer_12345_wallet).
4

Create accounts

For each logical balance, create a ledger account (customer BRL account, treasury account, provider fee expense account, merchant settlement account).

3. Setting up Account Types


Account Types are templates for the accounts in your ledger. They define how accounts behave — allowed operations, internal vs. external classification, and reconciliation rules — and let you classify accounts according to your financial structure. A typical setup for a payments product:
  • CASH → liquid customer funds
  • SETTLEMENT → funds awaiting clearing
  • FEE_REVENUE → fees collected
  • FEE_EXPENSE → provider fees
  • TREASURY → internal operations
For how to enable Account Type validation, the behavior of the type field, and how to manage Account Types via the API, see Account Types.

Understanding balance buckets

Before wiring up two-phase flows, it helps to know that each balance tracks funds in distinct buckets. A balance amount is exposed through three fields:
  • available — funds free to be spent or sent right now. Debits and credits to a balance move this number.
  • onHold — funds reserved by a pending hold and not yet committed. They are removed from available but still belong to the account until the hold is committed or cancelled.
  • scale — the number of decimal places used to interpret the integer amounts (e.g. scale: 2 means available: 100 is 1.00). It defines precision, not a separate pool of funds.
The two-phase actions move value between available and onHold on the source balance:
ActionavailableonHold
DirectDebited (source) / credited (destination)unchanged
Hold↓ decreased on source↑ increased on source
Commitcredited on destination↓ released from source
Cancel↑ returned to source↓ released back to available
Revertrestored on both sides via a counter-transactionunchanged
For the full balance model — multiple balances per account, permission flags, overdraft, and history — see Balances.

4. Defining Accounting Entries (Rubricas)


Accounting Entries (Rubricas) map a transaction action to the ledger accounts it debits and credits. Rather than computing accounting classifications by hand for each movement, you register rubrics once and let Midaz resolve them automatically — recording the resulting routeCode and routeDescription on each operation. Rubrics are configured per action on each Operation Route, inside the accountingEntries block. Source routes require the debit rubric, destination routes require the credit rubric, and bidirectional routes require both.

The five action types

ActionCodeWhat it does
DirectdirectImmediate, single-step debit/credit between two accounts, no intermediate stages (e.g. a fee or adjustment).
HoldholdReserves funds by creating a pending movement (availableon_hold on the source).
CommitcommitConfirms a previously held amount, releasing on_hold to the destination.
CancelcancelCancels a hold, returning on_hold value to available on the source.
RevertrevertReverses a completed direct transaction via a counter-transaction.
Each action can point to different debit/credit mappings within the same rubric, so every stage of an operation lands on the right ledger entry. You register these through the Operation Route endpoints — see Create an Operation Route.
{
  "accountingEntries": {
    "direct": {
      "debit":  { "code": "1.1.1.001", "description": "Customer cash-out" },
      "credit": { "code": "2.1.1.001", "description": "External settlement" }
    },
    "hold": {
      "debit": { "code": "1.1.2.001", "description": "Pending settlement — hold" }
    }
  }
}
For the full model, see Accounting Entries (Rubricas).

5. Transaction routing


Routing is a two-layer system that resolves at runtime:
  • Operation Routes define the accounting logic for each leg of a transaction — which accounts are debited/credited, balance keys, validation rules — and carry the Accounting Entries (rubricas) above.
  • Transaction Routes define the business event that triggers accounting (PIX_CASH_OUT, WALLET_TRANSFER, BANK_SLIP_SETTLEMENT, …) and combine Operation Routes into a balanced financial event.
When a transaction is submitted, Midaz resolves the matching Transaction Route, then resolves each Operation Route and its rubric for the action being performed. It validates that balances exist, debits do not exceed available balance, assets match, and the ledger stays balanced — before anything is recorded. For route structure, fields, the operation-type validation matrix, and API behavior, see Transaction Routing.

6. End-to-end example — a Pix payment


Let’s tie it together with a simple Pix cash-out: a customer sends BRL out of their wallet to an external account.
1

Account setup

Create the accounts on your ledger:
  • customer_12345_brl — Account Type CASH, asset BRL
  • @external/BRL — the external settlement account for funds leaving the ledger
2

Rubrica registration

On the Operation Route for the customer leg (source), register the direct debit rubric. On the external leg (destination), register the direct credit rubric:
{
  "accountingEntries": {
    "direct": {
      "debit":  { "code": "1.1.1.001", "description": "Pix cash-out — customer" },
      "credit": { "code": "2.1.1.001", "description": "Pix cash-out — external settlement" }
    }
  }
}
3

Transaction

Submit a transaction against the PIX_CASH_OUT Transaction Route, moving, say, 100.00 BRL from customer_12345_brl to @external/BRL.
4

Resulting operations

Midaz records two balanced operations:
  • Debit customer_12345_brl 100.00 BRL, routeCode: 1.1.1.001
  • Credit @external/BRL 100.00 BRL, routeCode: 2.1.1.001
Both share the same transactionId, giving you a full trail from transaction → operation → rubric.
For a two-phase flow (hold → commit/cancel), register the hold, commit, and cancel rubrics on the route and submit the corresponding actions; each stage resolves its own rubric.

7. Validation modes


By default, if no rubric is registered for an action, the transaction proceeds normally and the routeCode/routeDescription fields are simply left empty for that operation — no error is raised. This graceful mode is convenient while you are still onboarding routes. In production, set accounting.validateRoutes to true in the Ledger Settings to enforce that every action has a registered rubric:
{
  "accounting": {
    "validateRoutes": true
  }
}
With strict mode enabled, any operation whose action and direction has no registered mapping is rejected with 0117 ErrAccountingRouteNotFound. This prevents movements from silently landing without an accounting classification.
Use strict mode (validateRoutes: true) in production ledgers where every transaction type must be accounted for. Keep the graceful default only while onboarding routes.

Next steps


  • Review the Accounting overview and reference pages for full field-level detail.
  • Once your ledger produces structured postings, see Lerian Reporter to transform ledger events into reconciliation files, financial statements, and COSIF-aligned regulatory outputs.