Rentals (add-on)
The properties let by the night, their stay rules, the rate card, and the quote for a stay.
Provided by the KahunaCart Rentals plugin. Rentals declares no permissions of its own: a property is a Bookings bookable and a stay is a Bookings booking, so reading takes kahunacart.bookings.view and changing anything takes kahunacart.bookings.manage. Everything a guest does goes through the storefront booking routes under their mode key. See Rentals for developers.
List the properties
/api/v1/kahunacart/rentals/properties
Every property in this store that books by the night: its Bookings service id, what the product and option are called, how many it sleeps, the zone its dates are in, what a night costs in the store's own currency before any rate band, and how many bands its rate card has.
Response
Answers {properties: [...]}: every service whose mode is nightly.
Read a propertys stay rules
/api/v1/kahunacart/rentals/rules/{serviceId}
One property's stay rules: what is stored, the same thing with the defaults filled in, how many it sleeps, its timezone and what it is called. Includes the note that goes out before somebody arrives, which often carries a door code and is never sent anywhere else. On a store that sells in more than one currency it also carries currency_amounts: what the cleaning fee, the extra-guest rate, the damage deposit and the monthly rate are in each other currency.
Parameters
| Name | Type | Description |
|---|---|---|
| serviceId required | integer | The Bookings service id of the property. |
Response
Answers {rule, effective, service_id, product_id, variant_id, product_title, variant_title, sleeps, timezone, currency}.
Save a propertys stay rules
/api/v1/kahunacart/rentals/rules/{serviceId}
Change a property's stay rules — check-in and check-out times, the shortest and longest stay, which weekdays a stay may start and end on, how many guests the nightly rate covers, the fees, the deposit terms, the cancellation policy and the pre-arrival note. Only the fields sent are changed. Refused in a sentence when a rule would contradict another. The _minor fees are in the store's own currency; currency_amounts gives the same four in each other currency, and a fee left empty in a currency is not charged there.
Parameters
| Name | Type | Description |
|---|---|---|
| serviceId required | integer | The Bookings service id of the property. |
| check_in_time optional | string | Wall time in the property's own zone, as `16:00`. |
| check_out_time optional | string | Wall time in the property's own zone, as `10:00`. |
| min_nights optional | integer | The shortest stay, in nights. |
| max_nights optional | integer | The longest stay, in nights. Zero is no limit. |
| arrival_days optional | array of integer | Which weekdays a stay may start on, `0` Sunday to `6` Saturday. An empty list is any day. |
| departure_days optional | array of integer | Which weekdays a stay may end on. An empty list is any day. |
| guests_included optional | integer | How many guests the nightly rate covers before the extra-guest rate starts. |
| cleaning_fee_minor optional | integer | Charged once per stay, in minor units. |
| extra_guest_minor optional | integer | Per guest per night over `guests_included`, in minor units. |
| damage_deposit_minor optional | integer | A stated amount the owner collects themselves. Never charged by the store. |
| monthly_minor optional | integer | What one whole month costs, in minor units. A stay of a month or more is priced a month at a time from the arrival date, and the nights after the last whole month by the rate card. 0 is no monthly rate. Raise max_nights (or set it to 0) so a stay can run past a month. |
| avoid_gaps optional | boolean | The orphan-night rule — refuse a stay that would leave nights nobody could book. |
| deposit_percent optional | integer or null | How much of the stay is taken today. Null takes the whole amount at once. |
| balance_days_before optional | integer or null | How many days before arrival the rest falls due. |
| pre_arrival_text optional | string | The note sent a few days before somebody arrives — directions, the check-in time, the door code. It is never put on a receipt, in an earlier message, in a feed or in any public payload. |
| currency_amounts optional | object | Only on a store that sells in more than one currency. The fees in each other currency, as decimals in that currency's own places: `{"EUR": {"cleaning_fee": "130.00", "extra_guest": "22.00", "damage_deposit": "450.00", "monthly": "1100.00"}}`. An empty string takes an amount away, a key left out leaves it alone, and one wrong figure refuses the whole save. An amount nobody gave in a currency is a charge that is not made there, never the store currency's figure. |
Response
Answers the same body as the read, after the save. Only the fields sent are changed.
Read a rate card
/api/v1/kahunacart/rentals/rates
One property's rate card: every dated band in position order, lowest first, with the property's own nightly price and currency beside them. The band furthest down the list wins a night that two bands both cover. On a store that sells in more than one currency every band carries currency_amounts and the card carries base_in, what the property's own price is in each other currency, which is null where nobody priced it there.
Parameters
| Name | Type | Description |
|---|---|---|
| service_id required | integer | The Bookings service id of the property. |
Response
Answers {service_id, currency, base_minor, timezone, rates: [...]}, lowest position first. The band furthest down the list wins a night two bands both cover, and base_minor is what a percentage band is a percentage of.
Add a rate band
/api/v1/kahunacart/rentals/rates
Add a dated band to a property's rate card. A band names either a price a night or a percentage of the property's own price, never both. With no position it goes on the end of the card, where it wins any night it overlaps. On a store that sells in more than one currency a band that charges a price a night needs one in each other currency, in currency_amounts, or the nights it covers are not for sale in that currency: a band with no amount there cannot price a night, and is never read as free.
Parameters
| Name | Type | Description |
|---|---|---|
| service_id required | integer | The Bookings service id of the property. |
| starts_on required | string | The first date the band covers, as `2026-08-01`. |
| ends_on required | string | The last date the band covers, inclusive. |
| label optional | string | What the merchant calls it, such as `High summer`. |
| nightly_minor optional | integer or null | A price a night in minor units — or `percent`, never both. |
| percent optional | integer or null | A share of the property's own price. 130 is thirty per cent more than usual, 80 is a fifth off. |
| weekend_minor optional | integer or null | What a weekend night costs inside this band, in minor units. |
| weekend_days optional | array of integer | Which nights count as the weekend. Empty is Friday and Saturday. |
| min_nights optional | integer or null | Overrides the property's shortest stay for a stay starting inside this band. |
| arrival_days optional | array of integer | Overrides the property's arrival days for a stay starting inside this band. |
| position optional | integer | Higher wins where two bands cover one night. |
| currency_amounts optional | object | Only on a store that sells in more than one currency. What the band charges in each other currency, as decimals in that currency's own places: `{"EUR": {"nightly": "230.00", "weekend": "265.00"}}`. `weekend` is only for a band that has a weekend price. A share of the property's own price takes none. An empty string takes an amount away, and one wrong figure refuses the whole save. |
Response
Answers 201 with {rate}. The body carries service_id.
Update a rate band
/api/v1/kahunacart/rentals/rates/{id}
Change one band on a rate card. Only the fields sent are changed. A band cannot be moved to another property. currency_amounts changes what it charges in the store's other currencies, with an empty string taking one away and a key left out leaving it alone.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | The rate band's own id. |
| starts_on optional | string | The first date the band covers. |
| ends_on optional | string | The last date the band covers, inclusive. |
| label optional | string | 190 characters or fewer. |
| nightly_minor optional | integer or null | A price a night in minor units — or `percent`, never both. |
| percent optional | integer or null | A share of the property's own price, between 1 and 1000. |
| weekend_minor optional | integer or null | At least 0. |
| weekend_days optional | array of integer | |
| min_nights optional | integer or null | At least 1. |
| arrival_days optional | array of integer | |
| position optional | integer | |
| currency_amounts optional | object | Only on a store that sells in more than one currency, as on `create_rental_rate`. |
Response
Answers {rate}.
Remove a rate band
/api/v1/kahunacart/rentals/rates/{id}
Take one band off a rate card. The nights it covered fall back to whatever band is under it, or to the property's own price.
Parameters
| Name | Type | Description |
|---|---|---|
| id required | integer | The rate band's own id. |
Response
Answers {deleted: true, id}.
Reorder the rate bands
/api/v1/kahunacart/rentals/rates/reorder
Renumber one property's rate bands in the order the ids are given, which is what decides who wins a night two bands both cover. Answers the whole rate card back. An id belonging to another property is refused and nothing is written.
Parameters
| Name | Type | Description |
|---|---|---|
| service_id required | integer | The Bookings service id of the property. |
| ids required | array of integer | The band ids, lowest priority first. Bands of this property the list does not name keep their order and follow the named ones. |
Response
Takes {service_id, ids: [...]}, renumbers position in that order and answers the whole card.
Quote a stay
/api/v1/kahunacart/rentals/quote
What a stay at a property would come to — every night with the band it fell in, the extra guests, the cleaning, the total, what is due today and when the balance falls due. A stay that cannot be had answers the reason a guest would have been given rather than an error. With currency it is priced in another currency the store sells in, from that currency's own amounts, and a stay with a night nobody priced there answers "Not available in EUR." rather than a price.
Parameters
| Name | Type | Description |
|---|---|---|
| product_id required | integer | The product the property is. |
| variant_id optional | integer or null | Which option the guest would have picked, where the product has options. |
| arrival required | string | The day they arrive, as `2026-08-09`. |
| departure required | string | The day they leave, as `2026-08-16`. They do not sleep this night. |
| guests optional | integer | How many people. One where nobody said. |
| exclude_booking_id optional | integer | A booking to leave out of the calendar, for quoting a move of an existing stay. |
| currency optional | string | A currency the store sells in, such as `EUR`. Left out it is the store's own. A currency the store does not sell in is refused. |
Response
Answers {quote}, refusal and all. A refused quote is a 200 with ok: false and the sentence a guest would have read, because nothing went wrong with the request. exclude_booking_id leaves one booking out of the reckoning, which is how a move is priced. A product with no nightly rule is a 404.
Money is minor units, named *_minor. Every route that names a service checks that it exists and that its mode is nightly, so a rule written against a haircut is a 404. A refusal from the rule or rate repository is its own sentence, as a 422.