Skip to content

Occupancy pricing through Channex to the channels

What a rate plan’s sale basis, occupancy curve, age bands and package become on the way from GuestCraft (the PMS) through Channex to each channel. GuestCraft writes only to Channex; how Channex forwards a value to a channel is configured in the Channex channel mapping, outside GuestCraft. Product rules are in ADR 0027 and ADR 0029; the publisher is described in ARI publication.

Verified means read on the cited Channex page or in the dated Channex OpenAPI snapshot (packages/channex-simulator/src/openapi.snapshot.json, captured 2026-10-28). Unverified means no documentation says it: it needs a staging run or the channel’s own confirmation, as listed in the certification checklist. Simulator tests prove GuestCraft’s side only, never a channel’s behaviour.

GuestCraft setting What GuestCraft sends or requires in Channex Status
Sale basis per room A per_room Channex plan; one rate per night. A per-room plan has one occupancy option, “for maximum occupancy”. Verified: rate plans, snapshot RatePlans.RatePlan, Restrictions.RestrictionUpdate
Sale basis per person A per_person Channex plan in manual rate mode with one option for each adult count from 1 to the plan’s maximum (“Occupancy Option for each possible count of adult guests”), at most 18. Verified: rate plans, size limits
Occupancy curve (per adult or explicit) Not sent as a rule: GuestCraft computes each adult count’s absolute price every night and sends them all in rates: [{occupancy, rate}], “Alternative to rate for Rate Plans with multiple Occupancy Options”. A night with any count unpriced is sent as stop sell. Verified: ARI, snapshot Restrictions.OccupancyRate
Derived plan Computed by GuestCraft and sent as absolute prices for every occupancy; the Channex plan must not inherit or derive (no derived_option rate rules, inheritance off). Verified that the fields exist: snapshot RatePlans.OccupancyOption; the requirement is GuestCraft’s
Infant and child age bands Not sent. The property’s Channex hotel policy must give every age from 0 to 21 the category the bands give it (next row), and the Channex plan’s infant_fee and children_fee must equal the bands’ nightly fees (0 for a missing band), or the plan is not published. A plan without bands prices no child and leaves Channex’s fees and ages to the channels. Channex adds the fees “if guest is Infant [Children], based at Hotel Age Policy”. GuestCraft charges a band’s fee for each night of the stay: a declared local rule, not a verified equivalence with Channex. Fields verified: rate plans, snapshot RatePlans.RatePlan. Unverified: per night or per stay, an integer fee’s unit
Hotel policy ages Read, never written: the property’s hotel_policy_id (Properties.Property, “UUID of the property’s hotel policy, or null”), then GET /hotel_policies/{id}: a guest is an infant up to infant_max_age (at most 10) and a child up to children_max_age (at most 21), each nullable. Read at plan discovery, at mapping and on every publication run, stored with the ARI settings (agePolicy). Not read yet, no policy, or a null age a band needs: a plan with bands is refused at save, at mapping and at publication. Verified: snapshot GET /hotel_policies/{id}, HotelPolicies.HotelPolicy, Properties.Property.hotel_policy_id; hotel policies. Unverified: whether a channel applies the policy’s ages to the fees
Package (included services) Not distributed. Channex’s ARI has no package field (snapshot Restrictions.RestrictionUpdate); the price includes the services, and the folio splits them out on posting. Verified (absence of a field)
Channex reads back One rate per plan and night (Restrictions.RestrictionValues); GuestCraft assumes the main option’s and compares only that occupancy. The other occupancies cannot be read, so the nightly drift check republishes them on a rotating seventh of the horizon: every night within seven checks. Health warns drift_partial_occupancies. Field verified; which occupancy it is: unverified
OTA booking with children Channex sends occupancy.ages “with information about children ages”; GuestCraft stores them when there is one age from 0 to 17 per child, and never invents one. Channex’s separate infants count is not read. Verified: bookings
Channel Sale basis Occupancy curve Age bands (fees) Package
Booking.com Standard or occupancy-based pricing (OBP), chosen per connection; with OBP “you can map each occupancy of a room type”, one primary mapping sending the restrictions. Verified: Booking.com mapping, Booking.com API example Each Channex occupancy price maps to a Booking.com occupancy when the hotel uses OBP. Unverified: what Standard pricing takes from a per-person plan Unverified: how children_fee/infant_fee reach Booking.com Not distributed; a meal plan is a Booking.com rate setting. Unverified
Expedia Standard (“one price per rate”) or OBP (“each rate carries a price per occupancy option”); for OBP one mapping per occupancy, exactly one primary. Verified: Expedia API example As Booking.com: each occupancy price maps to an Expedia occupancy option on OBP. Verified for the mapping; delivery unverified Unverified: the page names only max_children Not distributed. Unverified
Airbnb One rate plan per listing: “you can send just 1 price only”; map the lowest occupancy. Verified: Airbnb mapping Not distributed: Airbnb takes “Guests Included” and a single “Price Per Extra Guest”, set in the Channex mapping’s pricing settings, the same for every extra guest. A per-adult curve with one extra-adult amount can be mirrored there by hand; an explicit curve cannot. Verified: same page, fact check 1.3 Unverified: the page says nothing about children, nor whether children count as extra guests Not distributed. Unverified
Agoda “Agoda mapping will always be multi occupancy”; without occupancy prices “you can map the same occupancy to all the different occupancies”. Verified: Agoda mapping Each occupancy price maps to an Agoda occupancy. Delivery unverified Unverified Not distributed. Unverified
Hostelworld Private rooms sell at the room type’s maximum occupancy (“If there is a Double Room you should send the 2 person price”); dorms at the one-person price. Verified: Hostelworld mapping Only one occupancy is used: the maximum for a private room, 1 for a dorm bed. Map the per-person plan’s matching occupancy. Unverified: how Channex picks the occupancy of a per-person plan Unverified Not distributed. Unverified
Other channels Depends on the channel’s Channex adapter; no Channex mapping guide documents occupancy for the rest. Unverified Unverified Unverified Not distributed. Unverified

A plan declares where it is sold (distribution): direct only, never published to Channex, or through Channex on a list of channel kinds. Channex’s public API lists no channel or channel mapping of a property (no such operation in the OpenAPI document, SHA-256 50d36d7e…), so the declaration is GuestCraft’s, and each kind is held to what its Channex mapping guide documents (CHANNEL_CAPABILITIES in packages/core/src/reservations/channel-capabilities.ts):

Channel kind Occupancy it can price Child and infant fees Source
booking_com Every adult count at its own price (occupancy-based pricing connection) Not documented: refused Booking.com mapping
expedia Every adult count at its own price Not documented: refused Expedia API example
agoda Every adult count at its own price (“always be multi occupancy”) Not documented: refused Agoda mapping
airbnb One price up to the guests included, then one constant amount per extra guest: “you cant have different price for 1st extra or 2nd extra” Not documented: refused Airbnb mapping
hostelworld One price, whoever stays Not documented: refused Hostelworld mapping
None declared Conservative: what every channel with per-guest pricing represents, one constant amount per extra adult Refused This document

A per-person curve is representable with one constant step when the base price applies up to the base occupancy and each adult above it adds the same amount (a per-adult curve without a fewer-adult discount, or explicit adjustments of 0 up to the base and equal steps after it). A percentage-derived plan on such a channel is refused (derivation): rounding would break the constant step. One assessment, distributionProblem, serves saving the plan (rate_plan.not_distributable with field), mapping it (rate_plan.not_distributable with the plan and field) and every publication run (the plan alone is stop-sold, health plan_channels_mismatch with the field in planIssues, while the other plans keep publishing); it covers every plan sharing the same pricing root and the property’s hotel policy ages. A plan sold direct only cannot be mapped.

What GuestCraft refuses at save time, and why

Section titled “What GuestCraft refuses at save time, and why”

A plan is saved only if Channex can represent it exactly (rate_plan.not_distributable, or rates.invalid for a value that is wrong for any channel):

  • More than 18 adult counts (occupancy/max): Channex sells at most 18 occupancies.
  • A maximum above the room type’s adults, or a base outside 1–maximum (rates.invalid): the per-person plan sells every count from 1 to its maximum.
  • More than one infant band or one child band, or an infant band older than the child band (ageBands): Channex has exactly one infant fee and one child fee per plan.
  • A child age above 17 (ageBands/<i>): the channels count guests from 18 as adults.
  • What the declared channels cannot represent (occupancy/curve, derivation, distribution, or ageBands for a child or infant fee above 0), as above.
  • Age bands the property’s Channex hotel policy does not share (ageBands), on a property linked to Channex: the policy not read yet, no policy, or another category for any age from 0 to 21. A plan sold direct only, or without bands, is not compared.
  • A derived plan with its own curve or bands, or another sale basis than its root (rates.invalid): a derived plan prices every occupancy from its root’s (ADR 0027), so its Channex plan has the root’s options and fees.
  • What a published plan’s Channex plan holds (rate_plan.channex_locked, on sellMode, occupancy/max or ageBands): Channex fixes sell_mode when a plan is created, and its occupancy options and fees would no longer match, which stops the plan’s publication; a different setting needs a new plan and a new mapping.

A night whose curve leaves an adult count without a positive price is not refused at save time: that night is unsellable and is published as stop sell for the whole plan. Packages are not refused either: they are never distributed, and the price published includes them.

At mapping time, and on every publication run, the Channex plan must match the local plan (options, rate mode and, when the plan has bands, the hotel policy’s ages and the fees as above), and the plan must be distributable on its declared channels; health names plan_occupancy_mismatch, plan_age_policy_mismatch, plan_fees_mismatch or plan_channels_mismatch until it is.