Understanding payouts
This guide explains how to go from "the amount I received is not what I expected" to "here is the line that explains it", using the Payouts and Channel Payouts endpoints together.
Both endpoints require the payouts_read scope.
How a booking reaches a payout
A booking reaches a payout by one of two routes. Which one it took decides how many endpoints you need.
Direct booking — SmilyPay collected the guest's card payment. One hop, one endpoint:
traveller ──▶ Smily ──▶ property manager / owner
└── GET /payouts "what Smily paid you, and our fees"
There is no channel and no upstream to check. related_channel_payout_references is empty, and the whole
explanation is on the /payouts line. Skip to Direct bookings.
OTA booking — a channel collected and wired Smily a net amount. Two hops, and each endpoint covers exactly one of them.
traveller ──▶ OTA channel ──▶ Smily ──▶ property manager / owner
│ │
│ └── GET /payouts "what Smily paid you, and our fees"
└─────────────── GET /channel_payouts "what the channel wired, and their cuts"
The distinction matters more than it looks:
/payoutsanswers "why is this different from what was collected?" — it accounts for the fees Smily took between collection and payout./channel_payoutsanswers "why was that the amount collected?" — the OTA's commission, the city tax it withheld and remits itself, and any deduction it applied.
Smily cannot pay out money it never received. So if a payout looks low and /payouts shows no Smily fee
that explains it, the cause is upstream and lives on /channel_payouts. Pivot between them with
related_channel_payout_references on each payout item.
An OTA booking can also carry a SmilyPay payment. When the property manager sells an upsell on a channel booking and collects it through SmilyPay, that money will be paid out by Smily independently of the wiretansfer from an OTA for a given booking. Such a booking produces two lines in the same payout — one for each route — and needs both endpoints. See OTA bookings with a SmilyPay upsell.
A worked example
A booking sold through an OTA for 776.68, of which the channel withheld a city tax it remits itself.
Hop 1 — what the channel wired. GET /channel_payouts?booking_id=<id> returns a breakdown line:
{
"booking_id": 4711,
"payout_type": "RESERVATION",
"channel_price_in_cents": 77668,
"ota_commission_in_cents": 11370,
"ota_payment_charge_commission_in_cents": 1061,
"withheld_city_tax_in_cents": 1870,
"net_channel_payout_in_cents": 63367
}
Reading it: the traveller paid 776.68. The channel kept 113.70 commission and 10.61 payment processing, and withheld 18.70 of city tax to remit itself. It wired 633.67.
776.68 − 113.70 − 10.61 − 18.70 = 633.67
Hop 2 — what Smily paid out. The matching /payouts item:
{
"row_type": "channel_payout",
"booking_id": 4711,
"smily_pay_payment_amount_in_cents": 63367,
"smily_traveller_fee_in_cents": 1500,
"smily_payout_amount_in_cents": 61867,
"related_channel_payout_references": ["<channel payout reference>"]
}
Smily received the 633.67, deducted a 15.00 traveller fee, and paid out 618.67.
The two hops chain: 776.68 traveller → 633.67 wired → 618.67 paid out. Every reduction is named on
one endpoint or the other.
"My payout is smaller than I expected"
First, check row_type on the /payouts item. A smily_pay_payment line covers money SmilyPay
collected, so steps 3 and 4 do not apply to it — go to Direct bookings
instead.
Classify by row_type rather than by whether related_channel_payout_references is empty: carry-over and
reimbursement lines can be empty without being smily_pay_payment rows, and a smily_pay_payment row
can carry references when it covers an upsell on an OTA booking (see
below).
Then work through these in order. Each step ends at the field that proves the answer.
1. Is a Smily fee taking it? — /payouts
Find the item for the booking and read the smily_* columns: split payment, traveller fee, cancellation
protection, payment gateway. On a channel_payout row those fees are exactly what separates what the
channel wired from this line's contribution to the payout:
smily_pay_payment_amount − Σ(smily fees) = balance_in_cents
balance_in_cents is what this line contributes to the payout total, and it can be negative. That is
the point of the field: a booking can end up owing Smily money rather than paying property manager or owner.
A full refund where SmilyPay fees were already taken is the usual case — nothing is paid out, but the fee still stands,
so the line lands negative and is covered by the rest of the payout (or, if there is nothing to cover it
here, collected later as a carry-over line).
It is not the same figure as smily_payout_amount_in_cents, which is what was transferred to the
property manager and owner. See the Payouts overview for how
balance_in_cents is derived per row_type.
On a booking under split payment, both figures cover the property manager's and the owner's shares together, and the line appears in both parties' payouts with the same combined value — so the equation above describes the booking, not your gateway's share. See Direct bookings and split payment.
That equation closes whatever happened, because a fee column always reports what this payout actually funded — but that is not always the fee that was due. See A fee column shows what this payout could fund.
If that closes and the fees are the ones you expected, you have your answer and you are done.
2. Is it a carry-over from an earlier booking? — /payouts
Look for items whose row_type is gateway_fees_debt, traveller_fee_debt, split_payment_fee_debt or
cancelation_protection_debt. These pay nothing out and carry a negative balance_in_cents; they
reduce the payout total. See Carry-over lines below — and note that the booking they name is the
original one the fee was due on, not the booking being paid now.
A fee for a given booking is deferred only when the money Smily received for that booking was not enough to cover it or Smily received no funds for that booking. A fee that can be funded is taken on the booking's own line and never becomes a carry-over.
The mirror of this step is worth knowing too: if a fee on a booking's own line reads lower than you expected, the missing part may have been deferred to a carry-over line in a later payout. Search forward for a carry-over naming the same booking before concluding the fee was wrong.
3. Did the channel deduct against this booking? — /channel_payouts
GET /channel_payouts?booking_id=<id>, then read allocations. Two things to check:
- Is your booking listed as a source (
type: "booking", matchingbooking_id)? Then the channel clawed money back on this booking, andabsorbed_bynames the payouts that covered it. - Is your booking listed inside someone else's
absorbed_by? Then your booking was paid less to cover a different booking's negative movement.original_ota_amount_in_centsshows what your booking would have been paid before the recoup.
In the second case the booking whose payout looks wrong is not the booking that caused the deduction. Check both directions before concluding the figure is incorrect.
4. Was it an account-level movement? — /channel_payouts
An allocation with type: "account" is a deduction the OTA applied across the account rather than to any
reservation — an account-wide commission correction, for instance. It has a null booking_id, and it
still has to come out of the individual payouts, so it shows up in absorbed_by.
5. Do the channel's own numbers add up?
See When the numbers do not add up.
Direct bookings and split payment
A direct booking is one Smily processed directly — no OTA involved. SmilyPay collected the guest's card
payment, so all available details will be on /payouts, on an item with row_type: "smily_pay_payment",
payout_type: "Reservation" and an empty related_channel_payout_references.
OTA bookings with a SmilyPay upsell
row_type names how the money was collected, not where the booking came from. smily_pay_payment
means "SmilyPay collected this money" — which is true of every direct booking, and also true of an
upsell sold for an OTA booking.
When a property manager sells an upsell on a channel booking and collects it through SmilyPay, that payment goes through the same reconciliation a direct booking does. The booking therefore appears twice in the payout:
| Line | Covers |
|---|---|
smily_pay_payment | the upsell SmilyPay collected |
channel_payout | the net amount the OTA wired |
The smily_pay_payment line on such a booking does carry related_channel_payout_references, because
the booking really does have channel payouts.
Where the numbers come from
Two sources feed one line, and mixing them up is the usual reason a figure looks off:
smily_pay_payment_amount_in_cents,smily_payment_gateway_fee_in_centsandsmily_payout_amount_in_centsdescribe the money that settled in this payout.- The property-manager and owner amounts, and the traveller, cancellation-protection and split-payment fees, come from the booking's reconciliation — the record that decides how the collected money is distributed. These are the amounts the reconciliation recorded for the booking, so on a booking that settles across several payouts they do not necessarily describe this payout alone. Reconcile fees across every payout carrying the booking rather than reading one line in isolation.
balance_in_cents is the property-manager amount plus the owner amount. If that is zero, it is instead
the negative gateway fee: a booking that paid nothing out but still cost a gateway fee reduces the payout.
A booking can appear in more than one payout
Payments settle individually, so a booking whose money arrives in several payments produces one
smily_pay_payment line per payout, each carrying only what settled in it. A line that looks short
against the guest's total price is often complete for the payment it covers.
Reconcile across every payout that carries the booking, not against a single one. This can be a reason such a line looks smaller than expected.
When the split is not the configured one
When a booking's money is to be divided between the property manager and the owner, Smily reconciles it before paying out — working out how the collected money splits. That step can fail, usually because the booking is not fully paid, because it carries payments taken outside SmilyPay, or because the booking's source is not eligible for split payments.
The booking still pays out, but on a fallback distribution: the whole amount goes to a single party instead of being divided. Which party is configured per booking — it can be either the property manager or the owner, and one property manager can have different fallbacks for different rentals.
Split payment
With split payment, the SmilyPay payments on a booking are divided between the property manager and the owner instead of going to a single recipient. Two consequences when reading a line:
smily_split_payment_fee_in_centsis Smily's fee for performing the split. The reconciliation records how much of it came from each party, so the two sides may absorb unequal shares.- The property manager and the owner are paid through their own payment gateways, so their money
reaches them in separate payouts. Since
/payoutsis scoped to onepayment_gateway_id, a booking split between the two parties is visible from each gateway separately.
balance_in_cents and smily_payout_amount_in_cents are the property manager's and owner's shares
combined, and the same line appears in both parties' payouts. So Σ(item.balance_in_cents) will
not match amount_in_cents on a split-payment account, and you cannot read your own share off the line.
Reconcile against amount_in_cents per payout; per-party attributes are planned.
Carry-over lines
When the money Smily received for a booking is too small to cover a Smily fee — a cancelled or heavily refunded reservation, a channel that wired less than the fees due, or a line that nets to nearly nothing — the fee is not waived and not invoiced separately. It stays outstanding and is collected from a later payout, as its own line:
| Field | Value |
|---|---|
row_type | gateway_fees_debt, traveller_fee_debt, split_payment_fee_debt or cancelation_protection_debt |
payout_type | Smily carry-over amount collection — the same string for all four |
booking_id | the original booking the fee was due on, not the one being paid now |
smily_payout_amount_in_cents | 0 — this line pays nothing out |
balance_in_cents | negative — it reduces the payout total |
All four carry-over kinds print the identical payout_type, so it cannot tell you which fee is being
collected. Use row_type, or which fee column carries the amount.
Each kind puts its amount in the matching fee column, and leaves every other fee column null:
row_type | Column carrying the amount |
|---|---|
gateway_fees_debt | smily_payment_gateway_fee_in_cents |
traveller_fee_debt | smily_traveller_fee_in_cents |
split_payment_fee_debt | smily_split_payment_fee_in_cents |
cancelation_protection_debt | smily_cancelation_protection_fee_in_cents |
The other half of the pair
This is not a second charge. The same mechanism that records the carry-over reduces the fee charged on the original booking's line, so the original was charged less by exactly this amount. If you look at that booking's original payout line you will see the partial — or zero — collection.
That has a consequence worth building around: a fee column can read 0 because the fee was deferred,
not because none was due. Nothing on the original line marks it. The carry-over line in the later
payout is the only place the difference is stated, which is exactly why these lines exist — the
alternative would be paying out less on an unrelated booking with nothing in the response explaining it.
So the two halves only reconcile when you read them together:
original line: fee column shows what that payout could fund
carry-over line: the remainder, negative, in a later payout
── the two sum to the fee that was actually due
More than one fee can be short at once
When the money being reconciled cannot cover every fee due, each fee takes what it can and the remainder is deferred. One booking can therefore produce several carry-over lines, of different kinds, across later payouts — so finding one carry-over does not mean you have found them all.
A payout line reports what a given payout moved, not what a booking owes. To see the fees due on
a booking overall — read internal_fees on theBooking.
When the numbers do not add up
The decomposition on each /channel_payouts breakdown line is expected to account for the net:
channel_price − ota_commission − ota_payment_charge_commission − withheld_city_tax = net_channel_payout
Each term is sourced independently and none is derived from the others, so this is a check you can run rather than an identity that is guaranteed to hold. In practice it closes; the cases where it does not are known and listed below.
net_channel_payout_in_cents is always the amount the channel actually reported. Always use it as the
amount, and treat the other terms as the channel's explanation of it.
The known case where it will not balance is Booking.com correction and fee true-up rows. Such a row restates a gross and a city tax that cancel each other out, then wires a small difference:
{
"channel_price_in_cents": 4854,
"withheld_city_tax_in_cents": 4854,
"ota_commission_in_cents": 0,
"ota_payment_charge_commission_in_cents": 0,
"net_channel_payout_in_cents": -68
}
The declared terms sum to 0.00; the channel wired −0.68. That difference is real money and the row
reports it verbatim rather than hiding it in one of the other columns.
This example is a Booking.com line — it declares a payment-charge commission and a city tax, which is
only ever true of Booking.com. On Airbnb the same row would carry null for the payment-charge
commission and a cleaning fee instead.
null means "not known", not "nothing" — do not sum it as zero without deciding that is what you
want. One deliberate exception on Airbnb: ota_payment_charge_commission_in_cents is always null
because the channel has no such line, and there it genuinely is zero. See
How each channel fills these terms.
What you can verify independently, and what you cannot
Not every field on a breakdown line has the same provenance, and the response does not currently distinguish them. This matters if you are reconciling against your own records.
| Field | Where it comes from |
|---|---|
net_channel_payout_in_cents | Declared by the channel, passed through verbatim |
ota_commission_in_cents | Declared by the channel (Booking.com), or its per-payout host fee (Airbnb) |
channel_price_in_cents | Declared by the channel (Booking.com), or Smily's stored channel price (Airbnb) |
withheld_city_tax_in_cents | Declared by Booking.com — but on Airbnb, reconstructed by Smily |
Airbnb itemises neither the withheld city tax nor the channel price per payout. For Airbnb reservation movements Smily sources both from the booking's own records as they stood when that movement settled — the price the guest was charged, and the booking's withheld city-tax fees — and splits them across the movements of a multi-month or extended stay. They are our best reconstruction of what Airbnb kept, not figures Airbnb gave us.
Two consequences to code for. First, the split across movements is an allocation: for a stay settling in
several movements, each movement's share is apportioned using the other terms, so an individual line's
price and tax are our attribution rather than an independently attested figure — only their total across
the stay is. Second, Airbnb movements that are not reservations (resolutions, adjustments,
pass-throughs) carry no reconstruction at all and are served net-only, with these terms null.
The inputs to that reconstruction are not currently exposed on
Bookings Fees, so you cannot reproduce the Airbnb figure from
the API. If you need a number you can audit end to end, use net_channel_payout_in_cents.
Reconciling per booking, not per line
Breakdown lines are per movement, not per booking. A single booking can appear:
- several times in one channel payout — for example a stay extension collected as an extra payment, or a refund or adjustment the channel applied just after wiring the original amount;
- across several channel payouts over time — the monthly tranches of a long stay settle in separate payouts, as does an extension booked after the original settled;
- again later as a correction or a resolution, with a negative net.
So a booking-level question needs every movement, not the one in front of you:
GET /channel_payouts?booking_id=<id>
That returns every channel payout whose breakdown touches the booking. Sum across all of them before concluding anything about the booking as a whole.
Per-line and per-booking reconciliation can disagree. A channel correction may restate a figure an earlier payout already settled — each line is individually faithful to what the channel reported, while the booking's lines sum to more than the booking was actually charged. Reconcile at the level you actually care about, and expect the two to differ on corrections.
Beta caveats
Both endpoints are in beta and their shape may still change.
/payoutsreturns one payout per page. Rows may be computed on the fly, which is expensive, soper_pageis capped at1while in beta. Page through withpage.payment_gateway_idis required on every/payoutsrequest; omitting it returns400.- Both endpoints are read-only. Payouts and channel payouts are produced internally by Smily as OTA wires arrive; there is no way to create, update or delete them through the API.