Overview
A double-entry ledger for . Every transaction sums to zero.
New here? Read this first.
go-ledger has no user interface of its own. It is an API. The actual product is a set of HTTP endpoints your app calls to create accounts, move money, and read reports. There is no shipped dashboard, no login form, no admin panel. The API is the product.
This page is a demo. I built this small UI on top of those same APIs so you can click around and see what the ledger does, without writing any code. Every button and form here fires a real API call against the live server. Nothing is faked or hardcoded.
Want to see the API itself? Open the API Playground. Every endpoint used on this page is documented there, with example requests and responses you can send straight from your browser.
go-ledger is an open-source, multi-tenant, double-entry payment ledger written in Go. Money moves as balanced transactions: every set of postings sums to zero, so funds are never created or lost, only shifted between accounts. This demo is a thin client over the live API.
- Double-entry core zero-sum postings, balances derived from history, never edited in place
- Multi-tenant isolated ledgers, each with its own accounts, keys and policy
- Multi-currency the zero-sum invariant holds per currency, with FX conversion
- Operator surface tenants, API keys, webhook subscriptions and guardrail policy
- Reporting trial balance proof, transaction search and disputes
- Tamper-evident append-only audit chain and idempotent posting
Getting started, five steps
Accounts
The buckets money moves between. Balances are derived from postings, never edited directly.
All accounts
Double-entry sign convention: debits are positive, credits are negative, and every currency nets to zero across all accounts. Credit-normal accounts (income, liability, equity) therefore carry negative balances, so a growing income shows as a larger negative number. That is the balance working, not a mistake.
New account
Post a transaction
Move money as a set of postings. Positive amounts debit an account, negative amounts credit it. The set must sum to zero, so every posting here is in one currency. To move between currencies, use Convert currency.
Convert currency
Move money between two accounts in different currencies. The server applies the exchange rate and markup and posts the balanced legs for you.
How it works. Pick the account to take money from and the account to move it into. They must be in different currencies, and a rate for that pair must exist (set them in Settings). Enter the amount in the from account's currency, and the amount the other account receives is calculated for you.
Plain transactions stay single-currency. Post a transaction requires every posting in one currency (they must sum to zero). Crossing currencies is this page, because it needs a rate.
Statement
A running-balance view of a single account.
Tenants
Isolated ledgers. Each tenant has its own accounts, keys and policy.
All tenants
New tenant
API keys
Keys for . Secrets are hashed at rest and shown only once.
Keys
Issue a key
Webhooks
POST notifications when things happen in the ledger for .
Endpoints
Add webhook
Policy
Approval workflow (ADR-025)
Server-wide and read only here. A transaction that crosses a threshold is held; see it under Approvals.
Guardrails for . Everything here is optional. Saving writes the full policy: an empty field clears that guardrail.
Trial balance
The core invariant: every currency nets to zero across all accounts.
Per-account balances
Transactions
Everything posted to this tenant.
Audit log
Every change to the ledger, append-only and tamper-evident. Each row is hash-chained to the one before it, so a silent edit is detectable.
Disputes
Transactions under review. Funds stay reserved until resolution.
Approvals
Transactions held because they crossed the tenant's approval threshold (ADR-025). Nothing posts until a decision is made.
Settings
Local to this browser. Nothing here is sent anywhere except with your API calls.
API key
API key
This is the public demo. It uses the shared, rate-limited demo key automatically, so there is nothing to set here. On your own server this card lets you paste an admin key.
Workspace
Exchange rates
The price for turning one currency into another. Add one pair at a time.
How to add rates. A rate covers one currency pair. Fill in Base, Quote, and the Mid rate, then press Save rate. To add more pairs, just fill the form again with the next pair (for example USD to GBP, then EUR to BDT). Every pair you save shows up in the table above.
What is the mid rate? It is how many units of the Quote currency one unit of the Base currency buys. Example: Base USD, Quote EUR, Mid rate 0.9200 means 1 USD = 0.92 EUR. You only add one direction; the reverse (EUR to USD) is worked out for you automatically.
Spread override is optional. It is a per-pair markup in basis points (100 bps = 1%). Leave it empty and the pair uses the Markup fee default set below. Fill it to charge a different markup on just this one pair.
Demo note. In the public demo, every tenant is prefilled with USD-based rates for EUR, MYR, BDT, and INR plus a 1% markup. Those are a real snapshot captured once, not updated in real time.
Markup fee
Your cut on a currency conversion.
What it is. When someone converts money, the ledger gives them a little less than the true (mid) rate. That difference is your markup, your revenue on the conversion. It is set in basis points (bps): 100 bps = 1.00%, so 50 bps = 0.50% and 25 bps = 0.25%.
Example. The USD to EUR mid rate is 0.9200 and the markup is 50 bps. A customer converting 100.00 USD receives 100 x 0.9200 x (1 minus 0.0050) = 91.54 EUR. At the pure mid rate they would get 92.00 EUR, so the 0.46 EUR difference is your fee.
Where it applies. This default is used by any pair that has no Spread override of its own (see Exchange rates above). The global default applies to every tenant; a Current tenant override applies to just the selected tenant. Leave the field empty and Save to clear a tenant override so it follows the global default again.
About
go-ledger is an open-source, multi-tenant, double-entry payment ledger written in Go. Every transaction is a set of postings that sums to zero, so money is never created or lost, only moved. Source and docs at go.sohag.pro.