Asset types and accounts
Define each thing you count — a currency, a credit, a stock unit — with its own precision, then open accounts for it. Every account declares the lowest balance it may reach.
Product
Ledger is a hosted double-entry ledger with an API. Your systems post movements; the ledger keeps books that always balance, never forget and can be proved at any moment.
What you get
Define each thing you count — a currency, a credit, a stock unit — with its own precision, then open accounts for it. Every account declares the lowest balance it may reach.
A transaction lists where value comes from and where it goes. It is accepted whole or refused whole; a partial write is impossible.
Set money or stock aside without moving it, complete the hold when the sale goes through, or release it. Unfinished holds expire on a schedule you choose.
Read what is posted and what is available now, or the balance at any earlier moment. Statements are paged so a page never shifts under you.
Every write carries a key you derive from your own record. Send it twice and the second call returns the first answer; nothing moves again.
Give each client an isolated ledger under your account: their own accounts, their own records, their own keys. Suspend and restore them through the API.
The full API is published as an OpenAPI document, and a plain-text contract written for software agents is served beside it.
The ledger model
Your system posts a transaction with a key of its own choosing.
Balance, floors, account state and your key are verified under lock, all at once.
The entries are written permanently, or nothing is written at all.
Posted and available balances update in the same step; a background check keeps proving they add up.
A walk through the API
The guide shows every request and response; the runnable tour in the ledger repository performs them against a live tenant.
Step 1
One credential, scoped to your tenant. What it can see and do is decided by its role: read, post, administer, or manage clients.
Step 2
Create an asset type such as GBP with two decimal places, a control account for money entering the system, and customer accounts floored at zero.
Step 3
Post a transfer of 100.00 from the control account to a customer. The response shows each account’s available balance after the move.
Step 4
Send the same request again with the same key: the original answer comes back and the money does not move twice. Send a different body under the same key and it is refused.
Step 5
The balance now, the statement page by page, and the transaction itself.
Step 6
Place a hold of 25.00, then complete one hold and release another. Whatever you leave unresolved is released when it expires.
Step 7
An overdraft attempt, an unbalanced request and an invalid timestamp each return a clear code that names the field. Retry only the temporary ones.
Step 8
Ask for a record with another tenant’s credential and it simply does not exist. Nothing confirms that it is there.
POST /v1/transactions
Idempotency-Key: payout-2026-09-05-0001
Content-Type: application/json
{
"description": "welcome credit",
"entries": [
{ "account_id": "<control account>", "direction": "debit", "amount": "100.00" },
{ "account_id": "<customer account>", "direction": "credit", "amount": "100.00" }
]
}
201 Created (a second identical request answers
Idempotent-Replay: true <--- with the same body and this header)# money and goods in one write — or neither
POST /v1/transactions
{
"description": "sale: 2 widgets for 50.00",
"entries": [
{ "account_id": cash, "direction": "debit", "amount": "50.00" },
{ "account_id": customer, "direction": "credit", "amount": "50.00" },
{ "account_id": stock, "direction": "credit", "amount": "2" },
{ "account_id": shipped, "direction": "debit", "amount": "2" }
]
}
# unbalance either asset and the whole write is refused:
# 422 UNBALANCED_TRANSACTION{
"error": {
"code": "INSUFFICIENT_AVAILABLE",
"message": "insufficient available",
"details": [{
"account_id": "019fdfe1-…",
"available_minor": "7500",
"balance_floor_minor": "0"
}]
}
}
# how to react, by class
# 409 + Retry-After -> wait, then retry with the SAME key
# 422 / 404 / 403 -> a fact about your request; fix it or surface it
# 5xx -> temporary; your key is not consumedRead the full walk-through in the getting-started guide.
Implementation path
1
Request access; we provision a tenant and mint your first key. A partner key lets you create ledgers for your own clients.
2
Choose asset types and precision, decide which accounts are customer-facing (floored at zero) and which are control accounts.
3
Derive each request key from your own document, such as an order or statement line, so a retry can never double-post.
4
Pull statements and as-of balances into your reporting; the ledger keeps proving its own trial balance in the background.
The API surface
This table is produced from the live OpenAPI document and checked against it before every deploy, so it cannot drift from what the service actually serves.
| Group | Method | Path | What it does |
|---|---|---|---|
| Accounts and balances | GET | /v1/accounts | List your accounts, page by page. |
POST | /v1/accounts | Open an account for an asset type, with its floor. | |
GET | /v1/accounts/{account_id} | Read one account. | |
PATCH | /v1/accounts/{account_id} | Change an account: freeze it, close it, adjust its floor. | |
GET | /v1/accounts/{account_id}/balance | The current balance: what is posted and what is available. | |
GET | /v1/accounts/{account_id}/balance-as-of | The balance at any past moment, on either clock. | |
GET | /v1/accounts/{account_id}/entries | The statement: every entry on the account, in order, page by page. | |
| Reference data | GET | /v1/asset-types | List the asset types you have defined. |
POST | /v1/asset-types | Define something you count: a currency, a credit, a stock unit, with its precision. | |
| Partners and sub-tenants | GET | /v1/sub-tenants | List your clients. |
POST | /v1/sub-tenants | Create an isolated ledger for one of your own clients. | |
PATCH | /v1/sub-tenants/{sub_tenant_id} | Suspend or restore a client. | |
GET | /v1/sub-tenants/{sub_tenant_id}/keys | List a client’s keys, with when each was last used. | |
POST | /v1/sub-tenants/{sub_tenant_id}/keys | Issue a key for a client. | |
DELETE | /v1/sub-tenants/{sub_tenant_id}/keys/{key_id} | Revoke a client’s key immediately. | |
| Transactions and holds | POST | /v1/transactions | Post a balanced transaction, or place a hold. |
GET | /v1/transactions/{transaction_id} | Read one transaction and its entries. | |
POST | /v1/transactions/{transaction_id}/commit | Settle a hold. | |
POST | /v1/transactions/{transaction_id}/void | Release a hold. |
Explore it live: API explorer · OpenAPI document · llms.txt for software agents · API reference.
Tell us what you keep books of and we will set up a tenant, mint your first key and walk you through the tour.