Ledger

Alpha

Move money and track money in real time.

A double-entry ledger for your product. Every movement is recorded on both sides, balances update as it happens, and history can't be rewritten.

Every entry balances, or it isn't recorded.

Pick an entry to see how a payment, a refund and a transfer are each recorded as debits and credits that add up to zero.

  • Balanced entries
  • Immutable records
  • Period locks
Journal entryExample · USD

Payment received

$120.00

Funds received and credited to the customer’s balance.

Payment received: example debit and credit entries in US dollars
AccountDebitCredit
Cash$120.00—
Customer balance—$120.00
Total$120.00$120.00
BalancedDebits equal credits

Payment received. Total debits $120.00, total credits $120.00. Balanced.

Every transaction records both sides of the movement.

Transaction rules

Rules the ledger enforces

Every guarantee below is enforced at the database layer. Here's exactly what the API sends back when something tries to break one.

Built on Astrum, our open-source ledger core
  1. Balance

    Balanced, per currency, or rejected.

    Debits must equal credits in every currency on the transaction. One currency can never balance another, and the database enforces it, not your app code.

    unbalanced_transaction

    POST/v1/transactions

    Idempotency-Key: order-1001-payment

    {
      "entries": [
        { "account_id": "acct_cash", "side": "debit", "amount": "10000" },
        { "account_id": "acct_revenue", "side": "credit", "amount": "9000" }
      ]
    }

    422Unprocessable Entity

    {
      "type": "urn:astrum:error:unbalanced_transaction",
      "status": 422,
      "code": "unbalanced_transaction",
      "request_id": "8f2c1d0e-…"
    }
  2. Immutability

    Mistakes get reversed, never edited.

    Posted transactions can't change. A reversal posts the opposite entries as a new transaction that points back to the original, and each one can only be reversed once.

    already_reversed

    POST/v1/transactions/txn_01j9…/reverse

    Idempotency-Key: refund-ord-1044

    {
      "description": "Refund for ord_1044"
    }

    201Created

    {
      "object": "transaction",
      "id": "txn_01j9x…",
      "status": "posted",
      "reverses_id": "txn_01j9…",
      "entries": [
        { "account_id": "acct_revenue", "side": "debit", "amount": "2400" },
        { "account_id": "acct_cash", "side": "credit", "amount": "2400" }
      ]
    }
  3. Period close

    Closed periods stay closed.

    Close a period after you report on it and nothing can be posted into it again, including backdated entries in a batch. Reversals are dated today, so they never touch it.

    period_closed

    POST/v1/transactions

    Idempotency-Key: late-adjustment-7

    {
      "effective_at": "2026-08-31T23:00:00Z",
      "entries": [
        "…"
      ]
    }

    409Conflict

    {
      "type": "urn:astrum:error:period_closed",
      "status": 409,
      "code": "period_closed",
      "request_id": "1a7e44b2-…"
    }
  4. Idempotency

    Retries never post twice.

    Every money movement needs an idempotency key. Retry after a timeout with the same key and you get the original result back, not a second transaction.

    Idempotent-Replayed

    POST/v1/transactions

    Idempotency-Key: order-1001-payment

    {
      "entries": [
        "… same request, retried …"
      ]
    }

    201Created · replayed

    Idempotent-Replayed: true

    {
      "object": "transaction",
      "id": "txn_01j8…",
      "status": "posted"
    }
  5. Seal chain

    History you can verify.

    Every posting is sealed into a chain with a key kept outside the database. Ask the ledger to check its own balances and seal chain at any time.

    integrity_report

    GET/v1/integrity

    200OK

    {
      "object": "integrity_report",
      "ok": true,
      "issues": [],
      "chain_head": "c71a9e2044f1…"
    }

Money in motion

A payment, step by step

Holds, pending transactions and balance checks are built in. Watch one wallet move through a card authorization, a withdrawal and a top-up.

Timeline

wallet:alice · USD · normal side credit

openlock_version 1

PostedJournal balance

$50.00

PendingPosted + pending

$50.00

AvailableSpendable now

$50.00

HeldReserved by holds

$0.00

Where posted funds are

$50.00

  • Available
  • Held
  • Pending out

Entries

Account entries
MemoStatusAmountBalance after
Opening depositposted+$50.00$50.00

Starting balance: $50.00 posted. Pick a step or let it play.

FAQ

Frequently asked questions

Answers about the ledger, balances and the API. Ask us anything else.

  • A single balance column tells you what someone has, but not how they got there. It's easy to double-spend under concurrent requests, hard to audit, and one bad update can silently change history.

    A double-entry ledger records every movement as balanced entries, so money is never created or lost by accident. You get a complete audit trail, balances at any point in time, safe retries, and books your accountant can actually work with.