Ledger17
Double-entry bookkeeping as an API.
Ledger17 is a headless ledger: no screens, no workflows — a command API records immutable, balanced transactions and a query API serves balances, subledgers and the live journal. Your systems post the facts; the ledger keeps the books.
Built on CQRS and event sourcing. Writes append immutable events; reads hit pre-calculated views, so queries stay fast at any ledger size.
Post a transaction. Everything else is enforced.
Amounts are integer minor units. Every block must balance — the ledger refuses anything else.
$ curl -X POST https://api.example.com/ledger/v1/transactions \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: org-84120557" \
-H "X-Idempotency-Key: inv-2026-0042" \
-d '[{
"name": "Sales invoice #42",
"transactionDate": "2026-07-01",
"unit": "EUR",
"entries": [
{ "accountReference": "1500-receivables",
"entryType": "DEBIT", "amountMinorUnits": 125000,
"dimensions": { "CUSTOMER": "cust-001" } },
{ "accountReference": "3000-sales",
"entryType": "CREDIT", "amountMinorUnits": 100000 },
{ "accountReference": "2700-vat-out",
"entryType": "CREDIT", "amountMinorUnits": 25000 }
]
}]'| 1500-receivables | DEBIT | 1,250.00 |
| 3000-sales | CREDIT | 1,000.00 |
| 2700-vat-out | CREDIT | 250.00 |
| Σ debits = Σ credits | ✓ balanced | |
Blocks that don't balance exactly are rejected with 400 UNBALANCED_TRANSACTION. Once accepted, a block is immutable — corrections are new, compensating blocks.
Accounting guarantees, delivered over HTTP
Append-only, immutable
Transactions are events, stored forever. No updates, no deletes — corrections are compensating reversals, so the audit trail is the database.
Double-entry, enforced
Every transaction block must balance: Σ debits = Σ credits, exactly — integer arithmetic, no rounding. Unbalanced writes are rejected before they touch the books.
Fast, pre-calculated reads
Balances are aggregated as transactions post, not when you ask. Queries are lookups, not scans — response times stay flat as the books grow.
Idempotent writes
Every write carries an X-Idempotency-Key. Retry as hard as you like — the same key can never post twice.
Dimensions & subledgers
Tag entries with CUSTOMER, SUPPLIER, PROJECT and more. Rollups are pre-computed for every dimension in parallel; each query filters on one.
Live event stream
Subscribe to the journal over WebSocket and follow every posted block in real time — from any sequence number you choose.
Built for software — human optional
The same ledger, three ways in: an MCP server for AI assistants, API keys for unattended systems, and an admin console for your tenant.
MCP server for AI agents
Connect Claude or any MCP client to mcp.ledger17.com and the ledger becomes tools: balances, subledgers, reports — and balanced writes. The caller’s own permissions apply; the admin API is deliberately absent.
API keys for integrations
Unattended systems authenticate with an API key bound to one tenant, scoped to exactly the permissions the integration needs — read:ledger for a dashboard, write:ledger for a posting system.
Admin console
Manage your tenant, subscription and API keys at admin.ledger17.com. Everything else stays headless — the console administers access; your systems and agents keep the books.
Command writes. Query reads.
All endpoints live under /ledger/v1. Tenant scoping via X-Tenant-ID, OIDC bearer tokens in production.
Record transaction blocks, submit business events, reverse mistakes. Accepts facts; derives and validates entries via domain rule sets.
- POST
/ledger/v1/transactionsrecord balanced blocks - POST
/ledger/v1/transactions/{id}/correctionscompensating reversal - POST
/ledger/v1/eventssubmit a business event - POST
/ledger/v1/events/batchbulk import (up to 10 000) - GET
/ledger/v1/eventsinquire submitted events
Read the books: balances, transaction lines, subledger rollups, party exposure and the live journal — served from pre-aggregated views, fast at any size.
- GET
/ledger/v1/balancesaccount balance for a period - GET
/ledger/v1/transactionstransaction lines - GET
/ledger/v1/subledgersubledger rollup by dimension - GET
/ledger/v1/parties/{partyId}/exposurenet exposure per party - WS
/ledger/v1/eventslive journal stream
