Verifying prior ledger states

Immutability is the most important guarantee from a ledger. At Modern Treasury, we guarantee immutability by keeping track of the historical changes whenever a Ledger object is updated, which is backed by a series of immutable event logs. In this guide we will walk through how to verify prior ledger states by finding the historical changes to a Ledger object.

Ledger Transaction History

Ledger Transactions are created to record money movement events that happened among multiple Ledger Accounts. A Ledger Transaction can be updated when the status is pending, but once it transitions to the final status posted or archived, it can no longer be changed. Throughout the life cycle of a Ledger Transaction, each modification generates a new Ledger Transaction Version with an incremental version number starting from zero.

Ledger Entries of a pending Ledger Transaction can also be updated. In order to guarantee immutability, the old Ledger Entries are discarded while new Ledger Entries are created. The Ledger Transaction Version object not only stores the top level Transaction attributes like description, status, metadata, it also captures the historical Ledger Entry values when the change happened.

Querying Ledger Transaction Versions

The Versions of a Ledger Transaction can be retrieved by sending a request to the List Ledger Transaction Versions endpoint:

curl --request GET \
     --url https://app.moderntreasury.com/api/ledger_transactions/d376dd4e-1c4f-4ba2-9b13-db63c04471e0/versions

The API response contains a list of historical values of the Ledger Transaction, sorted by version in a descending order. Each Version also includes the Ledger Entries attached to the Transaction at the time when the Version was created:

[
  {
    "id": "b3ea66d8-ffa9-44e6-96d2-591cdb998a5e",
    "ledger_transaction_id": "d376dd4e-1c4f-4ba2-9b13-db63c04471e0",
    "object": "ledger_transaction_version",
    "ledgerable_type": null,
    "ledgerable_id": null,
    "ledger_id": "a9d970da-207e-43da-b4d6-6e9ae01ba2cc",
    "description": "Restaurant bill",
    "status": "posted",
    "ledger_entries": [
        {
            "id": "0bbdf6db-2378-449f-b731-0409c14f270c",
            "object": "ledger_entry",
            "amount": 5000,
            "direction": "credit",
            "ledger_account_id": "063da2e3-4a37-4abf-8626-57d25bd6d441",
            "ledger_account_currency": "USD",
            "ledger_account_currency_exponent": 2,
                        "ledger_account_lock_version": 5,
            "ledger_transaction_id": "d376dd4e-1c4f-4ba2-9b13-db63c04471e0",
            "live_mode": true,
            "created_at": "2022-10-15T16:58:51Z",
            "updated_at": "2022-10-15T16:58:51Z"
        },
        {
            "id": "5f6a2fe9-502c-439a-a475-2af09b8836ef",
            "object": "ledger_entry",
            "amount": 5000,
            "direction": "debit",
            "ledger_account_id": "f1c7e474-e6d5-4741-9f76-04510c8b6d7a",
            "ledger_account_currency": "USD",
            "ledger_account_currency_exponent": 2,
                        "ledger_account_lock_version": 5,
            "ledger_transaction_id": "d376dd4e-1c4f-4ba2-9b13-db63c04471e0",
            "live_mode": true,
            "created_at": "2022-10-15T16:58:51Z",
            "updated_at": "2022-10-15T16:58:51Z"
        }
    ],
    "posted_at": "2022-10-15T16:58:51Z",
    "effective_at": "2022-09-30T16:58:51Z",
    "metadata": {},
    "live_mode": true,
    "created_at": "2022-10-15T16:58:51Z",
    "version": 10
  },
  ...
]

Versions can be queried by their created_at timestamps. For example, to find the Ledger Transaction changes between 9am and 6pm UTC on Oct 21st, you would add query parameters created_at[gte]=2022-10-21T09:00:00Z&created_at[lte]=2022-10-21T18:00:00Z:

curl --request GET \
     --url https://app.moderntreasury.com/api/ledger_transactions/d376dd4e-1c4f-4ba2-9b13-db63c04471e0/versions&created_at[gte]=2022-10-21T09:00:00Z&created_at[lte]=2022-10-21T18:00:00Z

Versions can also be queried by a version number range. For example, if you’d like to see what a Ledger Transaction looked like when it was first created, you would add query parameter version[eq]=0:

curl --request GET \
     --url https://app.moderntreasury.com/api/ledger_transactions/d376dd4e-1c4f-4ba2-9b13-db63c04471e0/versions&version[eq]=0

Ledger Account Balance History

A Ledger Account has a lock_version field which increments when the pending or posted balance of the Ledger Account changes. Notice that you can add Ledger Entries to a Ledger Account with the effective_at timestamp in any order, but the lock_version increases per edit, which is perpendicular to effective_at .

Querying Ledger Entries

In order to find which Ledger Entry contributed to a certain Ledger Account lock_version, you can send a request to the List Ledger Entries endpoint with ledger_account_id and ledger_account_lock_version query parameters. As mentioned earlier, in order to guarantee immutability, previous Ledger Entries are replaced with the new ones upon editing, which is why we need an extra query parameter show_deleted=true:

curl --request GET \
     --url 'https://app.moderntreasury.com/api/ledger_entries?ledger_account_id=063da2e3-4a37-4abf-8626-57d25bd6d441&ledger_account_lock_version=5&show_deleted=true'

The API response contains the Ledger Entries of the ledger_account_lock_version:

[
    {
        "id": "b5cecf80-93ff-4e2a-8f37-fa5687a14837",
        "object": "ledger_entry",
        "live_mode": true,
        "amount": 6000,
        "direction": "credit",
        "ledger_account_id": "063da2e3-4a37-4abf-8626-57d25bd6d441",
        "ledger_account_currency": "USD",
        "ledger_account_currency_exponent": 2,
        "ledger_account_lock_version": 5,
        "ledger_transaction_id": "d376dd4e-1c4f-4ba2-9b13-db63c04471e0",
        "discarded_at": "2022-10-12T17:32:22Z",
        "created_at": "2022-10-11T19:40:59Z",
        "updated_at": "2022-10-12T17:32:22Z"
    }
]

Querying Account Balance

When a Ledger Entry with an effective_at timestamp is added to a Ledger Account, it affects the balance on and after the effective_at timestamp. For example, at 6pm UTC on Oct 1st, some Ledger Entries are added with effective_at as September 30th 6pm UTC. Then on Oct 15th, you would like see what September 30th 6pm UTC’s balances are as of now, you can send a request to the Get Ledger Account endpoint with a query parameter balances[effective_at]:

curl --request GET \
     --url https://app.moderntreasury.com/api/ledger_accounts/09fa6c2e-ab3e-42c8-86dd-6afbe7ef05ae?balances%5Beffective_at%5D=2022-09-30T18:00:00Z

The balances in the response are the sum of all the Ledger Entries that are currently recorded in the system with effective_at less than or equal to 2022-09-30T18:00:00Z.

{
    "id": "09fa6c2e-ab3e-42c8-86dd-6afbe7ef05ae",
    "object": "ledger_account",
    "name": "Alice's account",
    "ledger_id": "a9d970da-207e-43da-b4d6-6e9ae01ba2cc",
    "description": "Alice's account",
    "lock_version": 5,
    "normal_balance": "credit",
    "balances": {
      "pending_balance": {
        "credits": 50000,
        "debits": 10000,
        "amount": 40000,
        "currency": "USD",
        "currency_exponent": 2
      },
      "posted_balance": {
        "credits": 20000,
        "debits": 1000,
        "amount": 19000,
        "currency": "USD",
        "currency_exponent": 2
      },
      "available_balance": {
        "credits": 20000,
        "debits": 10000,
        "amount": 10000,
        "currency": "USD",
        "currency_exponent": 2
      } 
    },
    "metadata": {},
    "live_mode": true,
    "created_at": "2022-08-04T16:54:32Z",
    "updated_at": "2022-08-04T16:54:32Z"
}