Skip to content

Search documentation

Search documentation pages, sections, and topics.

On this page

How to build a marketplace ledger

Meet Firecnc, a fictional apartment-rental marketplace, think Airbnb. A guest pays Firecnc for a stay. Firecnc keeps a service fee, pays the payment processor, and pays the host the rest. Money moves constantly between you, your hosts, and your processor, and you have to know, to the cent, what you've earned and what you owe. This page builds that ledger end to end: design the accounts, post a booking, pay the host, and read the result. It's the pattern most Ledfra integrations follow.

Firecnc isn't real - we use a marketplace because it touches every part of the model at once: assets, liabilities, income, and expenses.

1. Design the structure

First you design Firecnc's chart of accounts in the Ledfra app (the ledger editor). The API doesn't create accounts - it refers to them by slug. Firecnc needs four:

SlugKindNormal balanceTracks
assets:stripeControlDebitCash held at your payment processor
income:service-feesControlCreditFirecnc's commission on each booking
expenses:processing-feesControlDebitWhat the processor charges to move the money
liabilities:hostsTemplateCreditWhat you owe each host, one account per host

The first three are control accounts: a small, fixed set that Firecnc as a whole shares. liabilities:hosts is a template: you define it once, and Ledfra creates a per-host account like liabilities:hosts/42 automatically the first time host 42 appears in a transaction, so you never pre-create an account per host. See Accounts for how the two kinds differ.

2. Write transactions

With the structure in place, you record what happens by posting transactions. Each entry names an account by slug, a DEBIT or CREDIT, and an amount. The one rule: within a transaction, debits must equal credits, which is double-entry (see Transactions).

A guest books a stay

A guest pays $230 for a booking. Of that, $200 is the host's and $30 is Firecnc's service fee. One transaction records the whole thing - cash comes in, and we split it between our revenue and what we now owe the host.

bash
curl 'https://ledfra.com/api/ledgers/{ledgerId}/transactions' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: booking-7af3-charge' \
  -H 'Content-Type: application/json' \
  -d '{
    "description": "Booking #7af3 — guest payment",
    "entries": [
      { "account": "assets:stripe",       "operation": "DEBIT",  "amount": "USD:230.00" },
      { "account": "income:service-fees", "operation": "CREDIT", "amount": "USD:30.00" },
      { "account": "liabilities:hosts/42", "operation": "CREDIT", "amount": "USD:200.00" }
    ]
  }'

The one debit ($230 in) equals the two credits ($30 + $200), so the transaction balances:

One booking, balancedThe booking transaction debits assets:stripe $230.00, which equals the credits to income:service-fees $30.00 and liabilities:hosts/42 $200.00.Debitsassets:stripe$230.00Total$230.00=Creditsincome:service-fees$30.00liabilities:hosts/42$200.00Total$230.00
Every transaction balances: total debits equal total credits.
Make writes retry-safe
The Idempotency-Key header means a network retry can't post the booking twice - the second call returns the original transaction. Use one stable key per real-world event.

Stripe takes its fee

The processor charges $7 to move the money. That lowers our cash and records an expense. It's a separate transaction posted to the same endpoint, and from here on we show just the request body:

json
{
  "description": "Booking #7af3 — Stripe processing fee",
  "entries": [
    { "account": "expenses:processing-fees", "operation": "DEBIT",  "amount": "USD:7.00" },
    { "account": "assets:stripe",            "operation": "CREDIT", "amount": "USD:7.00" }
  ]
}

Pay out the host

At checkout you pay host 42 the $200 you were holding. That settles what you owed and moves cash out.

Firecnc pays out directly here to keep the example short. Real marketplaces hold a host's earnings until the stay completes and the dispute window closes, then release them, which is what escrow accounting means in practice: a separate liability account for money you are holding but that is not yet payable, and a transaction for each transition between those states. See the holding-funds pattern in Transactions and How to design your ledger.

json
{
  "description": "Booking #7af3 — payout to host 42",
  "entries": [
    { "account": "liabilities:hosts/42", "operation": "DEBIT",  "amount": "USD:200.00" },
    { "account": "assets:stripe",        "operation": "CREDIT", "amount": "USD:200.00" }
  ]
}

3. Reports

You never add up balances yourself - Ledfra derives them from the entries as they're posted. After the three transactions, Firecnc's accounts read:

AccountBalanceHow it got there
assets:stripe$23.00230 in − 7 fee − 200 payout
income:service-fees$30.00Your cut of the booking
expenses:processing-fees$7.00The processor's cut
liabilities:hosts/42$0.00Owed $200, then paid it out

Those balances roll straight into the reports, with no assembly step:

  • Profit & loss - income $30 − expenses $7 = $23 profit. Note what is not here: the host's $200 never appears as revenue or as an expense, because it was never Firecnc's money.
  • Balance sheet - assets $23 = liabilities $0 + equity $23. The books balance, and the $23 earned is sitting in cash.
  • Trial balance - every account listed with its debits and credits, proving the whole set ties out.
  • Daily activity - these three transactions, with the day's totals around them.

Every one of those is built from the entries above. Nothing was exported, and there is no second set of books for you to keep in step.

What the API exposes today
The four reports are read in the Ledfra app - there are no report endpoints in the public REST API yet. Through the API you read an account's live balance with GET /ledgers/{ledgerId}/accounts/{slug}/balance, and the underlying history with GET /ledgers/{ledgerId}/transactions:
bash
# Read back everything that happened on the ledger
curl 'https://ledfra.com/api/ledgers/{ledgerId}/transactions' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Where to go next

  1. How to design your ledger - why the host's $200 is a liability, and the rest of the structural rules.
  2. Balance sheet - read what this ledger now reports.
  3. Sandboxes - run this whole flow against throwaway data first.
  4. Quickstart - the shortest version of this, on your own ledger.
  5. What is Ledfra? - the model this example is built on.
  6. Guide: Why marketplace accounting becomes so hard - the same business without a ledger, and what goes wrong as the rules grow.

For the building blocks, see Accounts, Transactions, and Money. Full endpoint details are in the API reference.