What the Ledger Records
Recurso includes a built-in double-entry ledger that automatically records journal entries for every financial event — payments, invoices, refunds, and more. Every transaction creates balanced debits and credits, giving you an auditable, real-time view of your financial position.Why Double-Entry?
- Accuracy — Every transaction balances to zero. If debits do not equal credits, the entry is rejected.
- Auditability — Full history of every financial movement, tied back to source objects (invoices, payments, refunds).
- Reporting — Generate balance sheets, income statements, and cash flow reports directly from the ledger.
- Compliance — Meets requirements for ASC 606 revenue recognition and standard accounting principles.
Account Types
Recurso organizes ledger accounts into five standard accounting categories:Built-in Account Codes
Recurso provisions the following accounts automatically for each tenant:Account codes follow standard chart-of-accounts conventions. Assets start with
1xxx, liabilities with 2xxx, revenue with 4xxx, and expenses with 5xxx.How Transactions Are Created
Recurso automatically creates journal entries when financial events occur. You never need to create entries manually — they are produced as a side effect of normal billing operations.Invoice Finalized
When an invoice is finalized, Recurso records the amount owed:Payment Received
When the customer pays the invoice:Revenue Recognized
As the subscription period elapses, the scheduler earns deferred revenue month by month:4000 Revenue directly, because the charge is earned the
moment it is billed.
Refund Issued
When a refund is processed:Tax Collected
The invoice entry (Code 1) posts the GROSS total to Accounts Receivable, so the tax portion starts inside Deferred Revenue. A separate reclass entry (Code 6) moves it to its own liability — note it debits Deferred, not AR, which already carries the gross amount:4000 Revenue instead.)
Discounts
Discounts do not post their own entry. A coupon reduces the invoice total before Code 1 posts, so the ledger simply records the smaller gross amount — there is nothing separate to reclass.Querying the Ledger
List Ledger Accounts
Retrieve all ledger accounts and their current balances.List Journal Entries
Query the postings that touch one account.account_id is required;
filter by posting code and page with limit/offset. Each row is one
balanced transfer — a single debit account, a single credit account, one
amount:
Entry Filter Parameters
reference_id is the business object that caused the posting (invoice,
payment, credit note, wallet transaction, …); there is no date-range
filter — page through by created_at order instead.
LedgerAccount Object
LedgerTransaction Object
Every transaction is exactly one debit and one credit — there is no
multi-line
entries[] array. A business event that touches several accounts
(an invoice with tax, say) posts several rows sharing the same
reference_id.
TigerBeetle Backend
For high-throughput environments, Recurso supports an optional TigerBeetle backend. TigerBeetle is a purpose-built financial transactions database that provides:- Sub-millisecond latency for ledger operations
- Strict serializability guaranteeing consistency
- Built-in two-phase transfers for complex settlement flows
- Throughput exceeding 1 million transactions per second
.env
Reconciliation
The reconciliation report answers “does the ledger agree with the billing records?” for your tenant. Run it on demand withGET /v1/finance/reconciliation;
a scheduler also runs it daily and logs
Ledger reconciliation found discrepancies when drift appears. The report
is computed fresh on every run and never persisted — it only reads, fixing
drift is a human decision.
- Billing records vs. PostgreSQL ledger — every non-draft invoice must have invoice postings summing to its total, every paid invoice must have payment postings summing to its amount paid, and every such posting must reference an existing invoice.
- PostgreSQL ledger vs. TigerBeetle (when connected) — the tenant’s Postgres ledger transactions (authoritative) are diffed against the enumerated TigerBeetle transfers by transaction ID.
Discrepancy types
tb_compared and tb_skip_reason
The TigerBeetle pass is best-effort and always honest: when it cannot run,
the report sets tb_compared: false with the reason in tb_skip_reason
instead of failing. Skip reasons you may see:
- TigerBeetle is not configured or the client is not connected (normal for Postgres-only deployments — Postgres is the authoritative ledger)
- The tenant has more than 100,000 ledger transactions or transfers, above the in-memory comparison guard
- Enumerating transfers or loading transaction summaries failed
tb_compared is true and tb_accounts_checked /
tb_transfers_checked report its scope.
The report lists at most 100 discrepancies (
truncated: true when more
exist); total_discrepancies always carries the full count.Watching ledger activity
There are noledger.* webhook event types. Every posting is written
synchronously with the billing event that caused it, so subscribe to the
business events instead (invoice.created, payment.succeeded, …) and
read the postings with the entries endpoint
when you need them.
Best Practices
Use Reference IDs
Always trace ledger entries back to their source invoice, payment, or refund using
reference_type and reference_idReconcile Regularly
Compare ledger balances against your payment gateway settlements weekly to catch discrepancies early
Monitor Imbalances
Set alerts on the trial balance. Any non-zero difference indicates a bug or data corruption
Archive Old Entries
Query with date ranges to keep responses fast. Export older entries to your data warehouse for long-term storage