The Financial System of Record: Why the Ledger Owns Truth
By Formance Staff
Connectivity
Ledger
Embedded Finance
Loading article content
Loading table of contents
Loading author
_FORMANCE LEDGER/
Test your financial system of record locally
Formance Ledger provides double-entry accounting, immutable postings, and metadata to enforce these invariants. You can test it locally by cloning it from GitHub and posting a $250,000 omnibus allocation.
The ledger owns truth because it enforces balanced, immutable, uniquely referenced postings at write time, so every other record can be checked against it. Every payments stack has a financial system of record or competing candidates for one. A sponsor bank, auditor, or regulator will eventually ask which record to trust.
Many engineering teams present auditors with conflicting numbers. The application table stores a balance column updated by the last webhook received, where a webhook is an event notification. The payment service provider (PSP) dashboard shows a settlement that the table never received. A retry fired twice, and the handler dropped the second attempt. The bank statement for the omnibus account is short of the sum of user balances.
Each month, the system reconstructs ownership of the account's funds by joining records across a dozen rail-specific tables. No one can say which number the auditor or regulator should use.
A single financial system of record prevents conflicting balances from becoming disputes. Engineers who know why the ledger holds that role can tell an auditor or regulator which number to trust.
What is a financial system of record and how does it differ from a source of truth?
A financial system of record is the system where the official transactional posting is written. Every other system either reads from it or reconciles against it.
A source of truth is the record you trust when two records disagree. The two roles can sit in different systems. A stack can write postings in one system while trusting another system's numbers, and that split is the problem. In a financial stack, both roles belong to the core ledger, because the ledger is the only system that writes and enforces the postings. Trusting any other record means trusting a copy of the ledger.
The ledger records what your business believes happened. The bank statement and PSP reports are outside evidence of what the rails did. When they differ, the ledger remains the record you correct, and reconciliation finds the differences and records them as new postings.
If the bank statement is your source of truth and the application table is your system of record, your own records are a cache of someone else's, and you cannot attribute a dollar in the omnibus account to a user without asking the bank.
The company's enterprise resource planning (ERP) system sits above the core ledger and receives summarized journal lines. The core ledger feeds the general ledger, but the general ledger never feeds back. The ledger earns both roles by enforcing the four invariants below, starting with zero-sum transactions.
Formance is an open-source, programmable core ledger that unifies fiat and digital assets with regulatory-grade traceability. We built the Formance Ledger to serve as the system of record for regulated entities running omnibus account structures, and it enforces these invariants in the ledger's write path rather than in application code.
Four ledger invariants that make a financial system of record authoritative
A ledger owns the authoritative record of financial transactions by enforcing four properties at write time: zero-sum transactions, balances recomputed from postings, immutable postings, and posting-level idempotency.
1. Zero-sum transactions keep every posting balanced
The posting function refuses any write that does not net to zero across accounts. Value enters internal accounts only from a boundary account that mirrors an outside party (a bank, say) and leaves only through a boundary account. A user balance can never rise without a matching fall somewhere else. The boundary account's negative balance records money entering from outside.
A "money from nowhere" row is the first symptom of a table that is not a ledger. A credit is written on webhook arrival with no debit, so the row says value appeared and nothing says where it came from. Double-entry accounting defines the rules for a balanced posting.
The write path must enforce the zero-sum check in the same database transaction that commits the postings, so a failing write rolls back everything.
2. Recomputed balances treat the balance column as a cache
Postings are the record, and you always derive a balance from them. A ledger balance column is a cache that speeds up reads, and is not the record.
Reconciliation and audit use balances recomputed from postings, and never the cached column. A cache updated by webhook handlers, retries, and manual fixes cannot show how it reached its value, so you should never compare it to a bank statement.
If a balance column disagrees with its postings, the column is wrong by definition, so the fix is to recompute the column and not to adjust it.
Rather than recomputing balances, the write path must write to the same account one at a time, so two concurrent debits cannot both read the same starting balance and overdraw it.
3. Immutable postings turn corrections into new postings
The ledger fixes an error by appending a reversing posting, and it never runs UPDATE on an existing posting. Using the database's UPDATE command to change a posting or balance row removes the evidence of what the ledger believed before the change. Auditors need to know what the record showed before the correction.
Citigroup's 2024 incident shows why immutable postings matter. An account was credited $81 trillion instead of $280, and auditors had to find and reverse the error after it posted. With immutable postings, a reversal is one more posting that nets the original to zero, and the history shows the error, the detection, and the fix in order.
Immutability uses two dates on each posting, known as bi-temporality, to track both when an event was recorded and when it was effective, keeping corrections honest over time. A posting carries an effective date (when the money moved) and a recorded date (when the ledger learned of it).
For a point-in-time query, the auditor specifies an effective date for the balance. The query returns either the postings known at that time or those known now, depending on the auditor's question. Hash-chaining each transaction to the one before it, where the ledger supports it, makes any edit to history detectable, and it is one component of ensuring ledger integrity.
Each posting carries a unique reference, and a uniqueness constraint on that reference prevents a replayed file from posting a settlement twice.
For postings from a payment rail, the reference comes from the rail's identifier, which can be a trace number, a settlement ID, or an on-chain transaction hash. For internal postings, such as an allocation to a user, it is derived from the internal allocation ID. The same source identifier must always produce the same reference.
The transaction's unique reference field provides the reference when the transaction is submitted, and you can store a copy as metadata to keep it queryable. A request-level idempotency key protects against retries while the key is retained. The client retries, the key matches, and the second write is dropped.
A duplicate rail event can also arrive in a clearing file a day later. That file carries no idempotency key, and the application programming interface (API) layer sees it as a new request, so without the uniqueness constraint the same settlement posts twice.
How omnibus account attribution allocates funds to each user
An omnibus account holds funds on behalf of many underlying owners. The bank's statement tells you the total but nothing about ownership, so the ledger splits the bank's single omnibus balance into an unallocated account plus one account per end user, and derives attribution from the postings.
Reconstructing attribution at report time instead requires a database join that combines records from the users table, the deposits table, and a rail-specific events table. Those tables may change between queries and may disagree, so an auditor cannot reproduce the result.
Each user has a separate account in a multi-segment account namespace, so no user's balance is computed from another user's rows. Numscript, Formance's purpose-built language for describing financial transactions, states the intent of each movement in terms both engineers and finance teams can read.
The account path encodes ownership. The boundary account @platform:banks:a:main mirrors the bank, records cash entering through bank_a, and may run negative.
Funds received but not yet attributed sit in @escrow:banks:a:unallocated, the unallocated account that awaits allocation.
Each user's attributed share sits in @users:<id>:available.
The account paths identify the bank, and transaction metadata identifies the rail. The same account paths serve wire, card, and automated clearing house (ACH) settlement.
With the accounts initially empty, a $25,000,000 wire moves from the boundary account to the unallocated account. Later, $150,000 moves from the unallocated account to one user's available account.
Post the receipt and allocation as separate transactions. The shared transfer_id links the two postings.
Each posting has its own unique reference. The receipt's reference derives from the wire's rail identifier; the allocation's derives from the internal allocation ID. Each is passed as the transaction reference when the script is submitted, and the scripts below also store it as metadata:
// WIRE_RECEIPT// Event: a wire arrives at bank_a; nothing is attributed yetsend [USD/2 2500000000] ( source = @platform:banks:a:main allowing unbounded overdraft destination = @escrow:banks:a:unallocated)set_tx_meta("event_type", "wire_receipt")set_tx_meta("transfer_id", "trf001")set_tx_meta("external_reference", "trf001-receipt")set_tx_meta("bank", "bank_a")set_tx_meta("rail", "wire")// WIRE_ALLOCATION// Event: allocate part of the received wire to one end usersend [USD/2 15000000] ( source = @escrow:banks:a:unallocated destination = @users:48213:available)set_tx_meta("event_type", "wire_allocation")set_tx_meta("transfer_id", "trf001")set_tx_meta("external_reference", "trf001-allocation")set_tx_meta("bank", "bank_a")set_tx_meta("rail", "wire")
After both postings, @escrow:banks:a:unallocated holds $24,850,000 and the sum of @users:*:available is $150,000. The two balances total $25,000,000, which matches the bank-reported balance.
The per-user accounts form a financial subledger: a point-in-time view of each user's postings. To verify the ledger matches the bank, a team can check this invariant at any time.
In this formula (unallocated + Σ @users:*:available = bank-reported omnibus balance), Σ means the sum across user accounts.
Formance does not hold or move funds. All money movement relies on a connected payment provider, such as a bank or PSP, and the ledger records the result.
How ledger reconciliation detects drift without rewriting postings
Ledger reconciliation verifies that two sets of records from two different entities match. Your postings on one side and the bank's statement or the PSP's settlement report on the other.
The engineering team records each difference as a new posting and does not edit the disagreeing posting, and the posting history shows which side moved. The process establishes consistency with an outside party and does not prove the ledger right.
Reconciliation is required for late data. A rail file arrives two days late carrying settlements for Tuesday. You post the settlements with the effective date set to Tuesday and the recorded date set to now, as described in the immutable postings section. You post unmatched items in the file to a suspense account, which holds funds while ownership is investigated.
Barclays' information technology (IT) outage from January 31 to February 2, 2025, caused payments to fail. The Treasury Committee described the outage as an IT failure, “leading to problems for your customers including payment failures and balances being incorrect.”
Whatever balances the bank's systems displayed during those three days, the question afterward was which record to trust. A ledger's immutable posting history answers which balances were recorded, when, and what changed afterward. Reconciling postings against settlements confirmed by the rails shows which record agrees with the rails.
Formance Reconciliation, available in Enterprise, automates the matching with policy-based rules that pair internal records against external statements.
Financial system of record checklist for your next design review
Your design passes review when a financial system of record owns every balance. The six questions check whether your design meets that standard. Each question finds a place where a cache or dashboard could be acting as the record.
Running the checklist before an audit matters because a sponsor bank or regulator will ask which number to trust, and you can't defend a balance your postings cannot reproduce.
1Does the database enforce a zero sum for every write, rather than relying on application code?
2Is any reconciliation or audit run against a cached balance column instead of balances recomputed from postings? If yes, the stack is treating a cache as a record.
3Can a correction be made without an UPDATE that changes an existing row?
4Will processing the same payment rail file again post the settlements twice?
5Does the sum of user accounts equal the omnibus balance on demand, using postings without joining separate tables?
6What balance does the ledger show for any past effective date, and does the historical view remain available after a late file?
A "no" or an unsure answer to any of these questions indicates where a cache or dashboard is acting as the record.
The team must consistently derive each unique reference from its source identifier, which is the rail identifier for rail events and the internal allocation ID for internal postings, because the fourth invariant depends on it.