Skip to content
On this page

Escrow accounting for a marketplace

A marketplace collects $500 from a buyer, keeps $60, and pays $440 to a seller three days later. For those three days the $500 sits in the platform's processor balance. It is real money, in a real account, under the platform's control.

The question that decides whether your books are right is what the $440 was during those three days. Get it wrong and every debit still equals every credit, the ledger passes every check you have, and your revenue figure is more than eight times too large. This guide covers the accounts, the entries, and the four mistakes that produce a ledger which balances perfectly and reports the wrong numbers.

What escrow means when you are the platform

Search for escrow accounting and most of what comes back is about property. A title company holds a deposit between a buyer and a seller of a house, or a mortgage servicer collects monthly amounts for tax and insurance and pays them out annually. That is a regulated, licensed arrangement with its own statutory rules, and this guide is not about it.

The platform version is the same economic shape without the licensing: you take money from one party, hold it for a period, and pass some of it to another party. It shows up under many names - held funds, pending balance, seller reserve, buyer protection, release period - and it appears anywhere there is a reason not to pay out instantly:

  • A dispute or cancellation window. The buyer can still ask for the money back, so paying the seller immediately means clawing it back later.
  • Delivery or completion. The work has been paid for but not yet done.
  • A payout schedule. You settle weekly because settling per order costs more in fees than it saves in goodwill.
  • Risk holds. A new seller's first payouts are delayed while you build confidence they will not disappear.

Whatever the product calls it, the accounting is identical, and it is the accounting this guide is about. Nothing below requires a licence, a trust account, or a lawyer. It requires deciding which account the money sits in while you hold it.

The money is yours and it is owed, at the same time

Here is where most explanations go wrong, including some written by people selling ledgers. They describe held funds as money that “passes through” the platform, or money the platform “does not keep.” Both are false, and the error matters.

The platform does keep the cash. It is in the processor balance, it is an asset the platform controls, and in many businesses it earns interest for the whole holding period. Float is a real line of revenue for companies that hold money at scale.

What is true is that the platform owes it. The cash is an asset and the obligation is a liability, and both are true at the same instant. That duality is the entire reason double-entry exists. A single balance column cannot say “we have $500 and we owe $440 of it.” Two columns can, and they must, because those are the two numbers the business actually needs: how much money do we have, and how much of it is spoken for.

The test
Would you have to return this money, or forward it to somebody else, if things went normally? Then it is a liability from the moment it arrives, however long you hold it and however much interest it earns while you do. Only the slice you keep is income.

The entry that makes revenue eight times too big

Here is the entry a platform writes when nobody has thought about it yet:

text
DEBIT   assets:stripe            USD:500.00
CREDIT  income:sales             USD:500.00

Two lines, balanced, and it will pass every arithmetic check ever run against it. It is also wrong in a way that no amount of checking will surface: it says the platform earned $500 on an order where it earned $60.

On one order that is a rounding curiosity. On a year of orders it is the difference between a business that looks like it has $50M of revenue and one that has $6M, and those two companies raise money, pay tax, and get valued very differently. The mistake does not announce itself, because the books balance the whole time.

Here is the same payment recorded correctly:

text
# The buyer pays. Three accounts move, not two.
DEBIT   assets:stripe                     USD:500.00   # cash arrives
CREDIT  liabilities:escrow/9001           USD:440.00   # the seller's, and we hold it
CREDIT  income:commission                 USD:60.00    # ours: the only revenue

# The processor's fee is a separate cost, not a deduction from the sale.
DEBIT   expenses:stripe-fees              USD:14.80
CREDIT  assets:stripe                     USD:14.80

Three accounts move where the wrong version moved two, and the third one is the whole point. The $440 goes to a liability the moment it arrives, because that is the moment the obligation starts. Not when the order is confirmed, not when the payout runs. The obligation exists as soon as the money does.

The processing fee is deliberately a separate pair of lines rather than a deduction from the sale. It is a cost the platform pays to a vendor, and it has nothing to do with what the buyer paid or what the seller is owed. Netting it into the sale hides it from the expense report, which is the one report that exists to show you what you are spending.

Key escrow per user, not per order

Note the account slug: liabilities:escrow/9001, where 9001 is the seller's id. Not the order id.

The instinct is to model escrow per order, because that is how the product thinks. An order is held, an order is released, an order is refunded. It also matches the table you probably already have. But it makes the most common question you will be asked into an aggregation:

sql
-- How much of this seller's money are we holding right now?
-- With escrow keyed per seller, this is a balance lookup, not an aggregation.
select balance from account where slug = 'liabilities:escrow/9001';

Keyed per seller, “how much of this seller's money are we holding” is a balance you read. Keyed per order, it is a sum over every open order for that seller, which is a query somebody has to write correctly, and then write correctly again in the support tool, the payout job, and the seller-facing dashboard. The same figure computed in three places will diverge, and the only question is when you notice.

The order does not disappear - it goes in the transaction metadata, where it belongs and where it can still be filtered on. What changes is which of the two is the account and which is the attribute.

Use the seller's immutable id for the key, not a username or an email. A username that can be edited is not a key, and an account is referenced by posted history forever. If someone changes their handle a year from now, an account named after the old one cannot be safely renamed, because renaming it rewrites what the history says. Designing a chart of accounts that stays correct covers the naming rules in full.

The four states money moves through

Money in a marketplace is rarely in one of two states. It is usually in one of four, and each transition is worth its own posting:

text
collected  ->  held (escrow)  ->  owed (payable)  ->  paid out
  • Collected. Cash has arrived. The platform has it.
  • Held. The counterparty has not earned it yet - the order is unconfirmed, the work is undelivered, the dispute window is open.
  • Owed. They have earned it and it is ready to pay. Still a liability, but a different one, because the reasons it might not be paid have gone away.
  • Paid out. The money has left.

Collapsing states you actually have is the quiet version of this mistake. If your product holds funds for three days, a ledger that jumps from collected to paid out cannot tell you where the money was on day two. That sounds academic until a seller asks why their balance showed one figure on Tuesday and another on Wednesday, or until you need to know your total held-funds exposure on a date that has already passed.

Every one of those questions is answerable from balances at a past date when each transition is a posting, and requires a reconstruction when it is not.

Releasing escrow is not income

The order is confirmed. The dispute window closes. The money is now genuinely the seller's, and it moves from held to owed:

text
# The order is confirmed and the dispute window has closed.
DEBIT   liabilities:escrow/9001           USD:440.00
CREDIT  liabilities:payables-sellers/9001 USD:440.00

Both sides are liabilities. Nothing touches income, because nothing about the platform's earnings changed - the $60 commission was recognised at collection, when the platform did the thing it gets paid for.

The temptation here is real, because release feels like the moment the transaction completes. It is the moment the product marks the order done and sends everyone an email. But the platform earned its commission by matching a buyer to a seller and processing a payment, and it did that on day zero. Recognising revenue at release moves your income into a later period for no reason other than that a status column changed.

Paying out is not an expense

The money leaves for the seller's bank account:

text
# The money leaves. Nothing about revenue changes.
DEBIT   liabilities:payables-sellers/9001 USD:440.00
CREDIT  assets:stripe                     USD:440.00

A liability is settled and an asset goes down. No expense account is involved, and this is the mistake that hurts most, because it is so reasonable. Money left the building. That is what expenses are.

But an expense is value consumed. Paying the seller consumes nothing - it discharges a debt that was already on the books, recorded when the money arrived. Booking it as a “payout expense” counts the same $440 twice: once as a liability you owed and once as a cost you incurred. A platform that does this looks unprofitable no matter how well it is doing, because its largest expense line is money that was never its own.

The tell is a profit and loss statement where the biggest cost is roughly the size of your gross payment volume. If that is what yours looks like, this is why.

What a refund actually costs

The buyer cancels before the order is confirmed. The money goes back:

text
# Refunded before release. The commission is given back too.
DEBIT   liabilities:escrow/9001           USD:440.00
DEBIT   income:commission                 USD:60.00
CREDIT  assets:stripe                     USD:500.00

# The $14.80 is not reversed. The processor keeps it.
# It stays where it was posted: an expense you have already paid.

Escrow is released back, the commission is given back, and $500 leaves the processor balance. Look at what is missing: the $14.80. The processor returns the buyer's money and keeps its fee, so a refunded order is not a reversal. It is a reversal plus a loss, and somebody has to absorb it.

There are three candidates - the platform, the seller, or the buyer, who receives $485.20 instead of $500 - and most marketplaces use at least two depending on who caused the cancellation. Whichever you pick, it is a business policy that changes per transaction, which makes it data rather than a constant.

What it is not is an occasion to invent a “fee recovery income” account to cancel the expense. The fee was paid and it is gone. A cost you pay has no offsetting income, and creating one to make a line net to zero inflates both sides of your statements while telling you less than the two honest numbers did.

Why marketplace accounting becomes so hard follows refunds further, through partial refunds and per-order fee policies, and shows what each new rule costs when reports are reconstructed by query rather than read off balances.

A ledger that balances can still be wrong

Every entry on this page balances. So does the wrong one at the top. That is the uncomfortable fact underneath all of it: balanced and correct are different claims, and double-entry only guarantees the first.

Arithmetic errors are the ones a ledger catches for free. A posting that does not balance is rejected at write time, so the class of bug where a number is simply wrong mostly disappears. What remains is the class where every number is right and one of them is in the wrong account - and that error is invisible to every automated check, because there is nothing about it to detect. The debits equal the credits. The trial balance ties out. The balance sheet balances.

It shows up much later, and never as an accounting problem. It shows up as a revenue figure that nobody can reconcile to the bank, a payables total that does not match what sellers think they are owed, or a due-diligence question during a raise that takes three weeks to answer.

The only defence is structural: decide what each account means before money starts flowing into it, and make pass-through money a liability by construction rather than by convention. That decision is cheap on day one and expensive to unwind after a year of history has been posted against it.

What escrow accounting does not solve

Recording held funds correctly answers where the money is and who it belongs to. It does not answer several things people expect it to, and the gap between those two sets is where unpleasant surprises live:

  • It is not a regulatory position. Whether you may hold customer money at all, whether it must sit in a segregated or trust account, and whether you need a money transmitter licence are questions of jurisdiction and business model. A correct ledger records what you did. It does not make what you did permissible.
  • It is not custody. Your ledger says you owe a seller $440. Whether that $440 is actually available depends on your processor balance, your payout timing, and whether you have spent it. Those are the same number only if you keep them the same number.
  • It does not decide revenue recognition timing for you. This guide takes the common position that commission is earned at the point of sale. If your contract says otherwise - a subscription, a guarantee period, a performance obligation spread over time - the accounts are the same and the timing is not.
  • It does not convert currencies. If you collect in EUR and pay out in USD, holding both is straightforward and the exchange between them is a separate problem with its own gains and losses.
Where a ledger helps, and where it does not

Everything on this page works in any double-entry system, including one you build yourself on Postgres. Held funds as a liability, per-seller keys, a posting per state transition - none of it needs a vendor, and plenty of platforms run exactly this on their own tables.

What you are choosing is who maintains the guarantees. Build it and you own the balanced write, the immutability, the per-account balances and the statements that read from them. Buy it and you own the structure instead - which is the half no tool can decide for you, because whether a given $440 is a liability or income is a fact about your business.

Ledfra is a ledger that takes the structural side seriously: postings are rejected if they do not balance, accounts are addressed by readable slugs like the ones above, per-seller accounts are created on first use rather than provisioned in advance, and the balance sheet, profit and loss and trial balance are read from the same postings instead of rebuilt by query. What it does not do is bank connectivity, currency conversion, multi-entity consolidation, or deciding your tax treatment.

Where this goes next

If the structure above is new, the thing to get right first is which category each amount belongs to, because that decision is what makes revenue real or fictional. Create the ledger structure works through the five account categories from an empty ledger and covers the filing decisions this page applies. Model normal debit and credit accounts derives why each account has a side that makes it go up, which is what the debits and credits above are doing.

For the implementation: building a marketplace ledger end to end walks the full flow from collection to payout with the transactions written out, and storing money exactly covers why $14.80 is a string and not a float, which is the other way a balanced ledger quietly reports the wrong total.