Using Ledger Account Payouts to offset balances

When a Ledger Transaction is created, its Ledger Entries are added to the corresponding Ledger Accounts. Each Ledger Account accumulates many Ledger Entries over time, which contributes to the Account’s balance increase. As a result, it makes sense to pay out those Accounts in order to safely empty their balances. For example, a marketplace will pay a supplier the entire amount owed according to the supplier’s payables account balance.

Payouts are difficult to manage because Ledger Entries can be backdated into periods that had been paid out previously already. For example, when paying out a Ledger Account’s February balance in March, Ledger Entries with a January effective time could have been added during February after January’s payout was completed. These Ledger Entries should be paid out in March instead of being left behind. Additionally, itemizing Ledger Entries to their corresponding Payout for accounting is complex.

Using the ledger_account_payout solves these problems by giving you out-of-the-box snapshotting and itemization functionality. A ledger_account_payout is an object that creates a ledger transaction to safely offset the posted balance of a ledger account.

How Modern Treasury handles Ledger Account Payouts

Let’s use the same monthly payout example mentioned earlier. As a marketplace platform user, you pay out the supplier’s payables accounts on a monthly basis. Say today is February 2nd and you would like to pay out a supplier’s ledger account, you’d create a ledger_account_payout object through Modern Treasury’s Create Ledger Account Payout API.

Behind the scenes, Modern Treasury uses the criteria you provided through the API to gather the Ledger Entries for this payout and calculate the payout amount for you. It also creates a Ledger Transaction that reduces the Account’s balance as a result. Each Ledger Entry can be included in at most one Ledger Account Payout. This prevents you from paying out the same amount twice. The payout amount is computed asynchronously so that there is no limit on how many Ledger Entries can be processed per Payout.

Creating a Ledger Account Payout

When creating a ledger account payout, the payout ledger account's ledger entries are used to calculate the payout amount, and its balance is reduced as a result. The funding ledger account is used to send or receive funds, depending on the normality of the accounts. Each ledger entry of the payout ledger account can only be included in one ledger account payout, which prevents paying one ledger entry multiple times.

An optional effective_at_upper_bound can be provided as an extra threshold to decide which ledger entries the payout should include. For example, say today is 02/05/23 and you are paying out Alice's January ledger entries, you'd specify the effective_at_upper_bound as 2023-02-01T00:00:00.000Z, this way the February ledger entries are excluded in this payout. It also excludes any ledger entries that have been paid out already.

The status is set to pending by default, because the common workflow is to first create a pending ledger account payout, then move money based on the payout amount, and lastly set the payout to posted. But setting status directly to posted is also an option.

Modern Treasury processes Ledger Account Payouts asynchronously

Once a ledger account payout is created, its status is set to processing immediately. When a ledger account payout is still being processed, its amount and ledger_transaction_id are null. Once a ledger account payout is processed successfully, the status is updated to pending or posted based on the status provided when creating the payout and its amount and ledger_transaction_id are now available. Since the status is updated by Modern Treasury, an ledger_account_payout.finish_processing webhook event is emitted for the ledger account payout.

Updating a Ledger Account Payout

When a ledger account payout is in pending status, it can be updated through the Update Ledger Account Payout API. This is where you can set the payout to posted as mentioned above. You can also set the payout to archived if it was created by mistake. Once the payout is archived successfully, the ledger entries are detached from the payout, and they can be included in future payouts again.

Retrieving Payout related data

The payouts themselves can be retrieved through the Get Ledger Account Payout API and the List Ledger Account Payout API. To retrieve the itemized Ledger Entries of the payout, use the ledger_account_payout_id as a query parameter of the List Ledger Entries API. To retrieve the itemized Ledger Transactions of the payout, use the ledger_account_payout_id as a query parameter of the List Ledger Transactions API.

Migrating to the Ledger Account Payout API

If you have been tracking ledger account payouts through metadata, or outside Modern Treasury's system, this article provides solutions for migrating to the Ledger Account payout API.

Creating migration payouts

  1. Get a list of Ledger Accounts that you would like to pay out to. It's advised to start off with a small batch of Ledger Accounts first.

  2. For each Ledger Account:

    2.a. Create a Payout through the POST /ledger_account_payouts API with an effective_at_upper_bound cursor. Assuming that these Ledger Entries have been paid out by you in the past already, the status should be set to posted, and the skip_payout_ledger_transaction should be set to true so that the payout ledger account's balance will not be reduced by the migration payout. Please feel free to give the payout a description like “Payout for migration purpose only”. Once the Payout is created, please take notes of the Payout’s created_at timestamp from the API response. Since this is the first time you are using the Payouts API, it will scoop up all the historical Ledger Entries and include them in this Payout. As a result, the Payout amount is for backfilling purpose only, and you should NOT move money for the initial Payout.

// Example request:
// Say today is 2023-02-01 and you are about to pay out Alice's January ledger entries
    "description": "Alice's migration payout",
    "payout_ledger_account_id": "d44ad0d0-b3ea-4698-867d-09fe961e52a6",
    "funding_ledger_account_id": "3824af4d-4151-48f6-8e8b-bf859cbddbcd",
    "effective_at_upper_bound": "2023-02-01T00:00:00Z",
    "status": "posted",
    "skip_payout_ledger_transaction": true

2.b. Even though Ledger Account Payouts are processed on Modern Treasury side asynchronously, since you do not care about the payout amount during migration, you would not need to wait for the payout finish_processing webhook before moving on to your routine payout workflow.

2.c. For your routine payout workflow, if you have been using effective_at to query the Ledger Entries or Ledger Transactions, you would set it to the same value as the Ledger Account Payout's effective_at_upper_bound. If you have been using posted_at to query, you would use the Ledger Account Payout's created_at as an inclusive upper bound of posted_at.

After these migration payouts, the future payouts on these accounts should reflect the correct payout amount.

Double writing phase

  • Please make sure the payouts created in the previous step are in posted status instead of processing before moving forward. This can be done through consuming Modern Treasury's ledger_account_payout.finish_processing webhook event, or by listing the payouts with pagination and check their status.
  • When next Payout cycle comes around, you should repeat the steps above, but this time please also compare the Payout amount for each Account. If any discrepancies found, please reach out to Modern Treasury to resolve it. One heads-up is, the Ledger Account API does not take an effective_at lower bound. It is designed to prevent missing pay out back-dated Ledger Entries. If this is the root cause of the discrepancies, it is actually the desired behavior to catch any issues in the existing system.

Switch-over phase

  • Create Payouts through the POST /ledger_account_payouts API for future Payouts. It is now the new source of truth.