Why the money an OTA sends you never matches the booking total
A booking says €480. The bank credit says €412.37. Nothing is wrong, and that is exactly the problem: a system that only knows the gross amount will leave that payout unreconciled forever.
Three numbers, routinely treated as one
A channel booking carries at least three financial figures and they are not interchangeable:
- The gross. What the guest paid, and the only number most systems store.
- The commission. The channel's cut — and increasingly not one number either, since some channels bill a different rate on different stay types.
- The payment fees. Card processing, separate from commission, sometimes charged by the channel and sometimes by whoever collected.
What lands in your bank is the fourth figure: the net payout, which is the gross minus whatever the other two came to. Store only the gross and you have a booking ledger that cannot be reconciled against a bank statement, which means your actual revenue is a spreadsheet somebody maintains by hand.

Null is a real value, and zero is a claim
This is the part that quietly corrupts revenue reporting. When a channel does not tell you its commission — and plenty of integrations do not, for some stay types or some plan versions — there are two ways to store it.
As zero, which is a statement: this booking cost us nothing in commission. As null, which is a different statement: we do not know what this booking cost us.
A direct booking genuinely has a zero cut. A channel booking with a missing commission does not, and once the two are stored identically no report can separate them again. In Boris all four channel columns are nullable on purpose, and that is not laziness about defaults — it is the only way a margin figure can later say how much of itself is unknown.
Derive one side, never both
Providers disagree about which figures they send. The same reservation can arrive with a commission and no payout, a payout and no commission, both, or neither — and the field is called hostPayout, netAmount, payoutAmount, ownerRevenue or importoNetto depending on who you integrated with, sometimes nested inside a money or financials block.
Normalising that is unglamorous work, and there is exactly one rule worth stating: you may compute the side the channel omitted, and you must not compute both.
- Commission known → net is gross minus commission minus fees.
- Net known → commission is the remainder, and if that remainder rounds to zero it stays unknown rather than becoming a confident €0.00.
- Neither known → both stay null. The booking is still correct; it just does not claim to know its own margin.
Reconciliation has to accept two amounts per booking
Most matching logic compares a bank credit to the booking's outstanding balance. That works for direct bookings and fails for every channel booking, because the credit is the net.
The practical fix is small: when matching a transaction, treat the stored net payout as a second acceptable amount for the same booking, within a couple of cents of rounding tolerance, and flag which of the two amounts matched. In Boris that flag exists on the match itself — a credit matched on the net is a different piece of evidence from one matched on the gross, and when you are explaining a month to an accountant that distinction is the whole answer.
Two refinements earn their keep. Bound the match by date, so a €412.37 credit does not attach itself to a stay from eight months ago that happened to cost the same. And skip bookings that already have a non-cancelled invoice, because that money has a different home.
Batched payouts are the next wall
Per-booking net payouts solve the single-credit case. Channels that remit fortnightly in one transfer covering fourteen stays are a harder problem, and it is worth being honest about the order of operations: you cannot split a batch you cannot price. Storing the expected net per booking is what makes a batch resolvable at all, because the batch is then a sum you can search for rather than a number nobody can explain.
What to ask of a report before trusting it
If a dashboard shows you a take rate per channel, ask it two questions:
- How many bookings in this figure reported no commission at all?
- Does the net column use the reported payout, or the gross where the payout is missing?
Boris's channel margin counts the rows with an unreported commission separately and shows that count beside the rate, which is why the rate it displays is sometimes labelled a floor. A report that cannot tell you how much of itself is guessed is not more accurate than one that can — it is only more confident.
Frequently asked questions
Why do OTA payouts never reconcile automatically?
Because reconciliation usually matches a bank credit against the booking's outstanding balance, and a channel never pays the gross. Booking.com remits the amount minus its commission, sometimes minus payment processing fees, sometimes batched across several stays. The fix is to store the expected net payout per booking and accept it as a second valid amount for the same booking.
Is a missing commission the same as a zero commission?
No, and conflating them is the most expensive modelling mistake here. A direct booking genuinely has no channel cut. A channel booking whose commission was never reported has an unknown cut. Stored as zero, the second one inflates your margin and nobody can tell which rows are real.
Can you calculate commission from the net payout?
One side can be derived, never both. If the channel sent a commission, the net is total minus commission minus fees. If it sent a net payout, the commission is the remainder. If it sent neither, the honest value is null — deriving both means inventing the pair and reporting it as fact.
Why is a take rate over partial data misleading?
Because averaging unknown commissions in as zero drags the rate down. If 40 of 100 bookings never reported a cut, the 12% you see is a floor, not a rate. A revenue report has to say how many rows are missing data, or it is a confident number built on a guess.
