> ## Documentation Index
> Fetch the complete documentation index at: https://mcp.theoryofevolution.com.au/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Folios and payments

> Charges, payments and adjustments on a reservation: postCustomItem, postPayment, postAdjustment, two-folio bookings, and reconciling to a source total.

A reservation's money lives in one or more **folios**. These findings come from rebuilding source folios on a destination property so that each balance matched the original.

## What Cloudbeds charges automatically

When you create a reservation, Cloudbeds posts these itself. Don't post them again:

* **Room charges**, at the property's current rate plan (see [Rate differences](#rate-differences)).
* **Taxes and fees** set up for the rate.

**Commission was not posted automatically**, even with a commissioned source mapped. While the booking was `confirmed` there was no commission line, and the folio was gross. See [Commission](#commission).

## Which endpoint does what

| Endpoint | What it posts | Notes |
| - | - | - |
| `postCustomItem` | **Charges** (debits), with optional payments | The one you want for add-ons and fees |
| `postPayment` | A **payment** (credit) | `type` = a `getPaymentMethods` code. Works on a `checked_out` booking. **No idempotency of any kind** |
| `postCharge` | Despite the name, a **payment** | Not a charge. Don't use it for debits |
| `postAdjustment` | A **reduction** to rate, product, fee or tax | Positive `amount` = reduction. No reference ID |

## `postCustomItem`

`POST https://api.cloudbeds.com/api/v1.2/postCustomItem`. It took four live failures to get the format right:

1. "Parameter items is required" → the items go in an `items` array, not as flat fields.
2. "Invalid value given in 'items'. Array expected." → `items` must be **PHP-form-encoded** (`items[0][itemName]=…`), not a JSON string.
3. "Parameter appItemID is required" → every item needs an `appItemID`.

What works:

```
reservationID=123
referenceID=mig-123-items
items[0][appItemID]=mig-food-the-grille
items[0][itemName]=Food - The Grille
items[0][itemPrice]=123.00
items[0][itemQuantity]=1
```

* Item fields: `itemName`, `itemPrice` (unit price), `itemQuantity`. Optional: `itemSKU`, `itemCategoryName`, `itemNote`, `itemTaxes[]`, `itemFees[]`.
* `appItemID` is your own ID for the item type. A stable slug of the name works, so every "Food - The Grille" shares one.
* **`referenceID` makes the call idempotent**: Cloudbeds rejects a second call with the same `referenceID`. Use one per reservation per pass.
* Use the `payments[]` array rather than `itemPaid`, which forces the payment to cash.
* Items are grouped under a category named after the **API key's name**. You can route that category to a folio (see [Two-folio bookings](#two-folio-bookings)).

## Copying charges from a source booking

The source's guest-side charges split cleanly into two sets that don't overlap:

* **Add-ons**: `getReservationInvoiceInformation` → `reservationAdditionalProducts`.
* **Fees and surcharges**: `getTransactions` lines with `transactionType: "debit"` and `transactionCategory: "fee"` (e.g. "Credit Card Surcharge").

Net out **voids**: a +400 surcharge cancelled by a −400 line with `transactionCategory: "void"` and the same description should come to nothing. Skip deleted transactions. Together, add-ons plus fees matched what the guest paid to the cent on the bookings we checked.

The room itself is an accommodation charge, which the destination generates on its own. Never post it.

## Payments

* **Use the net paid amount (total − balance), not `balanceDetailed.paid`.** `paid` is gross of refunds: one checked-out booking had total 14,366.06, paid 14,466.06 and balance 0, because of a \$100 refund. Copying the gross figure overpays.
* Post charges and payments **before** setting status, since checkout is refused while there's a balance.

## Two-folio bookings

Bookings from a **billing agent** source get two folios, **Agent Main** and **Guest**, through the property's **Folio Configuration**. Typically the agent folio holds the room, on 30-day terms and often still owing, while the guest folio holds incidentals paid at departure.

What we found (confirmed live):

* **Neither API version exposes the folios on a reservation.** In v1.2, `getReservationInvoiceInformation` gives one combined invoice and a single sub-reservation. `postCustomItem` and `postPayment` take no folio ID. The v1.3 accounting API returned nothing for our property.
* **Charges can be routed by configuration.** In Folio Configuration, route the API key's item category (e.g. "Jason Imports") → **Guest Folio** and "Room Related Charges" → **Agent Main Folio**. Charges then split correctly as they're created, with no code change.
* **Payments can't be routed.** A payment goes on whichever folio you're "in". Through the API that's the **primary (agent)** folio, whatever the configuration's Credits section says. Nesting the payment in `postCustomItem`'s `payments[]` didn't help either. **An incidentals payment that belongs on the Guest folio has to be moved by hand** after import. Flag those bookings for staff.
* `getTransactions` doesn't say which folio a line is on.

Check whether a booking has two folios from the **source** and the property's folio configuration. Don't guess from the amounts: about half our bookings were two-folio, and single-folio bookings can have incidentals too.

## Commission

`postAdjustment` **can** post commission. An earlier conclusion that adjustments couldn't be posted through the API was wrong.

```
POST https://api.cloudbeds.com/api/v1.2/postAdjustment
reservationID=123
type=product
itemID=<the property's internal product id for "Hotel Commission">
amount=2625
notes=…
```

* `type` is one of `rate`, `product`, `fee` or `tax`.
* **A positive `amount` posts a reduction**: `1` posts −1.00.
* `itemID` must be the property's **internal product ID**, not the SKU or item code (those return "Incorrect adjustment type"). The commission product doesn't track stock, so `getItems` doesn't return it. We found the ID by adding the product to one booking by hand and reading it back from `getTransactions` (the line's `originID`). Ours was `1291044` on one property; it will differ elsewhere.
* Find the source's commission from its transactions: non-deleted lines with `transactionCode` `C01` or the description "Hotel Commission".
* **No `referenceID`**, so posting twice posts twice. Only re-run on a fresh reservation.
* Where it lands follows Folio Configuration: once the "Commission" category was routed to Agent, it landed on the Agent folio next to the room.

## Rate differences

The destination charges the room at its **current rate plan**, not at what the source booking was sold for. In our migration that was often 1.3–2× higher. Find the gap from **folio totals** (destination total vs source total), not balances, so you catch it in both directions:

* **Over-charged** (the usual case): reduce the room rate.
* **Under-charged**: raise the rate, then post the rest of the payment. A balance check misses this case, because you've only paid the smaller destination total.

`postAdjustment` with `type=rate` and a positive `amount` (no `itemID`) should apply the reduction, and does so in the UI ("Adjust Charge → Room Rate"). That leaves the nightly room lines as they were plus one "Room Rate" reduction line. We confirmed it in the UI but **didn't run it through the API at scale**; we fixed rates by hand.

A booking whose room was **comped** at the source (\$0 room) is still worth creating, because it holds the room. The destination charges full rate, so flag it loudly for manual correction.

## `postPayment` has no idempotency, and nothing downstream notices

`postCustomItem` has `referenceID`; `postPayment` has no equivalent. Send the
same payment twice and the folio carries it twice, which reads as a
reconciliation that ran twice and settled twice — worse than one that never ran,
because it looks correct.

Two things that help:

* **Carry your own one-time claim** before the call, keyed on something the
  caller cannot vary.
* **Refuse to post onto a folio with nothing outstanding**, checked against the
  **amount** rather than against zero. They are the same rule: posting more than
  is outstanding overpays, and a zero balance is that with nothing left at all. A
  partially-paid booking is the case nothing else catches, because its *total*
  still agrees.

## Reconciling against a partner's statement

Findings from matching remittance files to Cloudbeds bookings.

### Two different reference columns, and only one is on screen

A booking carries `source_reservation_id` (the source's own reference, which is
what the Cloudbeds UI shows) and `third_party_identifier`. They are not the same
field and they are not always the same value. We met a mid-stay upgrade that
Cloudbeds booked as a **second** reservation under the same agent reference: the
two rows looked different in the UI, because the collision was on
`third_party_identifier`, which is indexed and never displayed. If you join on a
partner's reference, join on all of them and report an ambiguity by name.

### `paymentCollect` does not tell you who takes the money

It reads as *collect* for channels that take the guest's money — but what
several of those pass on is a **virtual card the property charges**, so the
transaction goes through Cloudbeds' own payments and lands in the payout like
any other. Booking.com and Agoda/Priceline both behave this way. Deciding which
bookings belong to a payout from this flag puts whole channels on the wrong side
and manufactures findings in both directions.

### A refunded booking's folio is emptied, not left in credit

Where Cloudbeds payments reverses the guest's card, the booking is **cancelled
and its folio zeroed** — total `0.00`, balance `0.00` — while the money still
appears on the payout as a negative line. No figure the PMS carries equals the
refunded amount, so a row like that can never balance against the folio however
you widen the window.

That emptiness is *consistent* with a refund, not proof of it: a comp, a stay
not yet priced, and a folio billed elsewhere all look identical. We require the
booking to be **cancelled** as well as empty before reading it that way.

Other partners leave the credit **on** the folio instead, which is a figure you
can compare — so this is a per-source behaviour, not a Cloudbeds-wide rule.

### Compare on the right column, per partner

Some agents settle only what the guest has not paid. A guest who books through
such an agent and then extends pays for the extra nights themselves, on the
**same** reservation — so the folio comes to 203.95 while the agent remits
53.95. Those rows reconcile on the **outstanding balance**, not the stay's
total. Partners whose folios are settled at the point of sale read zero
outstanding, and comparing *those* on balance would make every line differ.
Pick the column per partner and say on screen which one you used.

### The payout export is a legacy `.xls`

Cloudbeds payments' export is an OLE2 compound file (BIFF8), not the ZIP-of-XML
that `.xlsx` is. They share an extension and nothing else, so a reader for one
will not open the other. Settlement runs about **two days after checkout**,
which is why one booking can be funded across two payouts — and why a booking
can be part-paid by a card payout and part-paid by an agent whose own run lands
later.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.