Marketplace
Ledgers tutorial for marketplaces
Overview
For this tutorial, we will design and create a ledger for a fictional company called Rarebnb. Rarebnb is a marketplace that connects homeowners looking to rent out unique properties with travelers seeking accommodations.
We will carry out the ledger implementation in four phases:
- Define the Account and Transactions
- Create the Ledger Objects
- Write the Ledger Transactions
- Read the Ledger Account Balances
Step 1. Define the Account and Transactions
Before we implement our ledger, we need to design our accounts and transactions.
Ledger Accounts represent the distinct balances that Rarebnb will want to track over time. They act as records of activity for specific aspects of the business, such as assets, liabilities, revenue, and expenses.
As a property rental marketplace, there are four fundamental Ledger Accounts Rarebnb will track.
Ledger Account Name | Normality | Purpose |
---|---|---|
Cash | Debit | Tracks total funds held in the Rarebnb operating account. |
Guest Receivable | Debit | Tracks the amount Rarebnb needs to collect from a certain guest. Each guest in the marketplace will have their own receivable. |
Owner Payable | Credit | Tracks the amount Rarebnb owes to a certain property owner. Each owner in the maketplace will have their own payable. |
Revenue | Credit | Tracks revenue generated by Rarebnb’s marketplace. |
Next, we will determine the Ledger Transactions needed to support our marketplace ledger. Ledger Transactions are tied to the events that happen during end-user workflows. For the purposes of this example, we will demonstrate a simple workflow with just three events.
Ledger Transaction | Debited Accounts | Credited Accounts | Notes |
---|---|---|---|
Guest Booking | Guest Receivable account(increase) | Owner Payable account (increase); Revenue account (increase) | A guest has booked a stay with a property owner. |
Guest Payment | Cash account (increase) | Guest Receivable account (decrease) | A guest pays the amount owed for their stay. |
Owner Payout | Owner Payable account(decrease) | Cash account (decrease) | A property owner is paid out for a guest’s stay. |
Step 2. Create the Ledger Objects
Now that our Ledger is designed, we can begin implementing it. The first step is to instantiate the Ledger object.
Create a Ledger using the Create Ledger endpoint (POST /ledgers
).
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledgers \
-H 'Content-Type: application/json' \
-d '{
"name": "Rarebnb Ledger",
"description": "Represents our funds and user balances"
}'
Modern Treasury will return a Ledger object with an id
to be used in subsequent steps.
{
"id": "<ledger_id>",
"object": "ledger",
"name": "Rarebnb Ledger",
"description": "Represents our funds and user balances",
"metadata": {},
"live_mode": true,
"created_at": "2025-03-18T10:32:04Z",
"updated_at": "2025-03-18T10:32:04Z""
}
Next, create the three Ledger Accounts from the previous section using the Create Ledger Account endpoint (POST /ledger_accounts
). For our example, we will use Guest X and Owner Y as the guest and owner, respectively, involved in the booking.
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_accounts \
-H 'Content-Type: application/json' \
-d '{
"name": "Cash Account",
"description": "Tracks Rarebnb cash",
"normal_balance": "debit",
"currency": "USD",
"ledger_id": "<ledger_id>"
}'
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_accounts \
-H 'Content-Type: application/json' \
-d '{
"name": "Revenue",
"description": "Tracks Rarebnb revenue",
"normal_balance": "credit",
"currency": "USD",
"ledger_id": "<ledger_id>"
}'
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_accounts \
-H 'Content-Type: application/json' \
-d '{
"name": "Guest X Receivable",
"description": "Tracks fees to be collected from Guest X for a stay",
"normal_balance": "debit",
"currency": "USD",
"ledger_id": "<ledger_id>"
}'
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_accounts \
-H 'Content-Type: application/json' \
-d '{
"name": "Owner Y Payable",
"description": "Tracks amount owed to Owner Y for hosting a guest",
"normal_balance": "credit",
"currency": "USD",
"ledger_id": "<ledger_id>"
}'
Modern Treasury will return the three Ledger Accounts that were created. Each has its own id
that can be later used in Ledger Transactions.
{
"id": "<cash_account_id>",
"object": "ledger_account",
"name": "Cash Account",
"description": "Tracks Rarebnb cash",
"ledger_id": "<ledger_id>",
"normal_balance": "debit",
"live_mode": true,
"lock_version": 0,
"balances": {<balance_array>},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata": {},
"created_at": "2025-03-18T12:09:24Z",
"updated_at": "2025-03-18T12:09:24Z"
}
{
"id": "<revenue_account_id>",
"object": "ledger_account",
"name": "Revenue",
"description": "Tracks Rarebnb revenue",
"ledger_id": "<ledger_id>",
"normal_balance": "credit",
"live_mode": true,
"lock_version": 0,
"balances": {<balance_array>},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata": {},
"created_at": "2025-03-18T12:09:24Z",
"updated_at": "2025-03-18T12:09:24Z"
}
{
"id": "<guest_x_account_id>",
"object": "ledger_account",
"name": "Guest X Receivable",
"description": "Tracks fees to be collected from Guest X for a stay",
"ledger_id": "<ledger_id>",
"normal_balance": "debit",
"live_mode": true,
"lock_version": 0,
"balances": {<balance_array>},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata": {},
"created_at": "2025-03-18T12:09:24Z",
"updated_at": "2025-03-18T12:09:24Z"
}
{
"id": "<owner_y_account_id>",
"object": "ledger_account",
"name": "Owner Y Payable",
"description": "Tracks amount owed to Owner Y for hosting a guest",
"ledger_id": "<ledger_id>",
"normal_balance": "credit",
"live_mode": true,
"lock_version": 0,
"balances": {<balance_array>},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata": {},
"created_at": "2025-03-18T12:09:24Z",
"updated_at": "2025-03-18T12:09:24Z"
}
Step 3. Write the Ledger Transactions
With the Ledger and Ledger Accounts created, we can begin recording activity by writing Ledger Transactions.
First, create a Ledger Transaction using the Create Ledger Transaction endpoint (POST /ledger_transactions
) to record that Guest X has booked a stay at Owner Y’s property. Rarebnb charges a 5% commission fee to the property owner. So, the Ledger Transaction will reflect that Guest X will owe us $200, of which $10 is revenue for Rarebnb and the remaining $190 is owed to Owner Y.
💡 Notice that the Ledger Transaction records revenue before payment has been received. This is because accrual accounting is used, which records revenue when it is earned (i.e. when a booking is made). The alternative, cash accounting, records revenue only when money moves (i.e. payment is received from the guest or payment is sent to the owner).
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_transactions \
-H 'Content-Type: application/json' \
-d '{
"description": "Guest X Booking",
"status": "pending",
"external_id": "<booking_id>",
"ledger_entries": [
{
"amount": 20000,
"direction": "debit",
"ledger_account_id": "<guest_x_account_id>"
},
{
"amount": 1000,
"direction": "credit",
"ledger_account_id": "<revenue_account_id>"
}
{
"amount": 19000,
"direction": "credit",
"ledger_account_id": "<owner_y_account_id>"
}
]
}'
This Ledger Transaction is created in a pending
state. When the stay actually occurs, the Ledger Transaction status
is updated to posted
.
Once Guest X completes their stay, their payment will be represented by a Ledger Transaction that records their payment and zeros out their due balance.
🗒️ Note: The following Ledger Transactions are created in a
pending
state. This is a common pattern for any real-world money movement. Once the transaction has settled at the bank, the Ledger Transaction is then updated toposted
.
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_transactions \
-H 'Content-Type: application/json' \
-d '{
"description": "Guest X Payment",
"status": "pending",
"external_id": "<guest_payment_id>",
"ledger_entries": [
{
"amount": 20000,
"direction": "debit",
"ledger_account_id": "<cash_account_id>"
},
{
"amount": 20000,
"direction": "credit",
"ledger_account_id": "<guest_x_account_id>"
}
]
}'
Finally, create the Ledger Transaction to record the payment to Owner Y. This zeros out Owner Y’s balance and records funds leaving the Rarebnb Cash account.
curl --request POST \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_transactions \
-H 'Content-Type: application/json' \
-d '{
"description": "Owner Y Payout",
"status": "pending",
"external_id": "<owner_payout_id>",
"ledger_entries": [
{
"amount": 19000,
"direction": "debit",
"ledger_account_id": "<owner_y_account_id>"
},
{
"amount": 19000,
"direction": "credit",
"ledger_account_id": "<cash_account_id>"
}
]
}'
Step 4. Read the Ledger Account Balances
Since activity is ledgered as it takes place, Rarebnb can query for various balance information at anytime. If, for example, Rarebnb needs to display Guest X’s pending due amount after booking in the app, Rarebnb can query the Guest X Ledger Account using the Get Ledger Account endpoint (GET /ledger_accounts/<guest_x_account_id>
).
curl --request GET \
-u ORGANIZATION_ID:API_KEY \
--url https://app.moderntreasury.com/api/ledger_accounts/<guest_x_account_id>
This would return the current balance for Guest X.
{
"id":"<guest_x_account_id>",
"object":"ledger_account",
"name":"Employer X Receivable",
"description": "Tracks fees to be collected from Guest X for a stay",
"ledger_id": "<ledger_id>",
"normal_balance":"debit",
"live_mode":true,
"lock_version":1,
"balances":{
"pending_balance":{
"credits":0
"debits":20000
"amount":20000
"currency":"USD",
"currency_exponent":2
},
"posted_balance":{
"credits":0
"debits":0
"amount":0
"currency":"USD",
"currency_exponent":2
},
"available_balance":{
"credits":0
"debits":0
"amount":0
"currency":"USD",
"currency_exponent":2
}
},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata":{},
"created_at": "2025-03-18T12:45:39Z",
"updated_at": "2025-03-18T12:45:39Z"
}
Notice that only the pending
balance reflects the pending Ledger Transaction that was created for Guest X’s Booking. See Ledger Balances for additional details on the types of balances.
Similarly, if the amount owed to Owner Y needs to be displayed after Guest X completes their stay, the Owner Y Ledger Account could be queried, producing the following result.
{
"id":"<owner_y_account_id>",
"object":"ledger_account",
"name":"Owner Y Payable",
"description": "Tracks amount owed to Owner Y for hosting a guest",
"ledger_id": "<ledger_id>",
"normal_balance":"credit",
"live_mode":true,
"lock_version":1,
"balances":{
"pending_balance":{
"credits":19000
"debits":0
"amount":19000
"currency":"USD",
"currency_exponent":2
},
"posted_balance":{
"credits":19000
"debits":0
"amount":19000
"currency":"USD",
"currency_exponent":2
},
"available_balance":{
"credits":19000
"debits":0
"amount":19000
"currency":"USD",
"currency_exponent":2
}
},
"ledgerable_type": null,
"ledgerable_id": null,
"external_id": null,
"metadata":{},
"created_at": "2025-03-18T12:45:39Z",
"updated_at": "2025-03-18T12:45:39Z"
}
Notice that all three balances reflect the amount owed since the Guest X Booking Ledger Transaction has been updated to posted
at this point.
Next Steps: Advanced Topics
Up to this point, we’ve covered the basics of creating Ledger Accounts and Ledger Transactions to record user actions in the Ledger, but we’re just scratching the surface of what it takes to build a Production-ready application ledger.
Below are some more Advanced Topics you should explore as you build out your ledger design.
Attaching Metadata
Modern Treasury Ledgers supports attaching free-form metadata
to most objects, in the form of key-value pairs. Here are some metadata
ideas that a marketplace company like Rarebnb could use:
Object | Metadata |
---|---|
Ledger | productID |
Ledger Account | accountType ; guestName ; guestId ; ownerName ; ownerId |
Ledger Transaction | transactionType ; guestId ; ownerId |
Modern Treasury Ledgers allows you to query based on metadata
. For example, the List Ledger Transactions endpoint can be used with both ledger_account_id
and metadata
parameters to find all Ledger Transactions for a specific Ledger Account that match certain metadata
criteria.
Defining Ledger Account Categories
Oftentimes, we need to retrieve a real-time balance that is the sum of all Ledger Accounts of a particular type.
For example, Rarebnb will often want to review the sum of all Owner Payables to understand their outstanding liabilities.
Ledger Account Categories are the solution for this. They enable Rarebnb to easily “roll up” balances that comprise many Ledger Accounts, providing real-time access to aggregate balances to meet UI or reporting needs. Below are some LACs that may be useful for a marketplace.
Ledger Account Category | Normality | Description | Contains |
---|---|---|---|
Total Receivables | debit | Sums balances across all Guest Receivable Ledger Accounts. Represents the total amount Rarebnb must collect. | Guest X Receivable balance; Guest Y Receivable balance; Guest Z Receivable balance; etc. |
Total Payables | credit | Sums balances across all Owner Payable Ledger Accounts. Represents the total amount Rarebnb must pay out. | Owner A Payable balance; Owner B Payable balance; Owner C Payable balance; etc. |
Updated about 12 hours ago