Hanzo Ledger
A double-entry ledger you post to over HTTP
Money here only ever moves: a posting names a source account, a destination account, an amount and an asset. There is no way to write a debit without its credit, because a one-sided entry is not something the API can express. Every account but one has to stay at or above zero, so a transfer that would overdraw is refused instead of recorded.
Every cent lands somewhere
A posting has two ends. There is no call that writes only one of them.
Balanced by construction
A posting is a movement from one account to another, so an unbalanced entry is not rejected by a check — it cannot be written down. New money enters through one account named world; every other account must stay at or above zero, and a transaction that would push one under is refused whole.
Safe to retry
Send an Idempotency-Key and a repeat returns the original rather than posting again. The ledger also fingerprints what you sent, so reusing a key with different postings is caught instead of quietly accepted — which is the failure that makes retries dangerous in the first place.
Nothing is edited
No endpoint changes a posted transaction. A mistake is corrected by a reversing transaction that points back at the original, so what happened and what you meant both survive. Entries can also be SHA-256 chained, each hashed over the one before it, which makes a later edit in the database detectable rather than invisible.
Numscript
Describe the movement instead of computing it. Send an amount from an account and split it across destinations by percentage, nesting splits inside splits. The whole flow posts as one transaction, so a fee that cannot be taken takes the payment with it.
Assets carry their own precision
An asset is a code plus the number of decimal places it uses — USD/2 for cents, and the same shape for points, credits or a token with eighteen. Amounts are whole numbers in the asset's smallest unit, so nothing rounds on the way in.
Balances arrive with the transaction
Each transaction records the balances it produced for the accounts it touched, so a balance is read rather than recomputed from the whole history. Aggregate across a subtree of accounts in one query.
One transaction, however many parties
# A split payment: the vendor is paid and the platform takes its cut,
# in one transaction that lands whole or not at all.
curl -X POST http://localhost:3068/v2/main/transactions \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order_42_payout' \
-d '{
"postings": [
{ "source": "users:001", "destination": "merchants:042", "amount": 9500, "asset": "USD/2" },
{ "source": "users:001", "destination": "platform:fees", "amount": 500, "asset": "USD/2" }
],
"metadata": { "order": "ord_42" }
}'
# Or say it once in Numscript and let the ledger do the arithmetic:
#
# send [USD/2 10000] (
# source = @users:001
# destination = { 95% to @merchants:042
# 5% to @platform:fees }
# )