Availability & Rates (ARI)
Once a listing is onboarded, its availability and rates are pushed to Channex. ARI is keyed by the stored
room_type_id / rate_plan_id, so the listing must be onboarded first. Pushes happen three ways:
event-driven (the normal path), a periodic drift correction, and a manual admin call.
Event-driven push (normal path)
The PMS already emits inventory.changed / rate.changed events to the distribution-events SNS topic
when inventory or rates change. ChannexAriEventConsumer subscribes via its own SQS queue and is a
batch listener: a poll's worth of messages is coalesced per (channel, listing) — touched properties
unioned, date range widened — and pushed as one availability + one restrictions call, with values re-read
from the database at push time.
Crash-safe: the SQS batch is acked only after the pushes succeed. If the instance dies mid-push, the messages were never acked and SQS redelivers them, so no ARI change is lost when an instance goes down. A push failure fails the whole batch for redelivery; pushes are idempotent, so reprocessing is safe.
Enable per environment: channex.sqs.enabled=true + channex.sqs.queue-name (defaults to the sandbox
queue). See Deployment & Config.
Drift correction
ChannexDriftSyncJob (opt-in, channex.drift.enabled=true; hourly by default) pushes a full ARI window
for every onboarded listing, so no missed event can desynchronize Channex forever.
Manual push
POST /api/v1/admin/channex/listings/{listingId}/ari?channelId=chnl_X&from=2026-06-10&to=2026-09-08
fromdefaults to today; past dates are clamped to today (Channex rejects past dates).todefaults tofrom+ 90 days; the window is capped at 365 days.
{
"listingId": "lst_0853ie",
"status": "SYNCED",
"messages": [
"availability ranges pushed: 6",
"restriction ranges pushed: 6",
"room types: 2",
"window: 2026-06-10 → 2026-09-08"
]
}
Range compression
Sending one value per date for a year is thousands of entries. Consecutive dates with identical values
are run-length encoded into a single date_from/date_to range, so a year of unchanged availability is a
handful of entries:
{ "values": [
{ "property_id": "5f8a…", "room_type_id": "7c2b…",
"date_from": "2026-06-10", "date_to": "2026-09-08", "availability": 3 }
] }
Availability
Built from Inventory and sent to POST /availability:
availability=inventory.quantityminus active cart holds for that property/date (cart_inventory_hold_unit), clamped at 0 — so a unit held in someone's cart is not also sold on an OTA.- A stop-sell day (
inventory.stopSell = true) is pushed asavailability: 0.
Restrictions (rate + stay rules)
Built from ChannelRate (+ Inventory) and sent to POST /restrictions, also range-compressed:
{ "values": [
{ "property_id": "5f8a…", "rate_plan_id": "a91d…",
"date_from": "2026-06-10", "date_to": "2026-06-14",
"rate": 12000, "min_stay_arrival": 2, "stop_sell": false }
] }
rate=channelRate.nightlyRate(falls back torate), in the smallest currency unit (paise for INR).min_stay_arrival=inventory.minimumStay;stop_sell=inventory.stopSell.- Channex applies partial updates, so a rate-only edit can send just
{rate}without clobbering the stay rules.
Batching
Each values array is chunked at 500 entries per request, so large windows split across calls
automatically.
Verifying
Don't trust the 200. The doctor command reads ARI back from Channex
(GET /availability, GET /restrictions?...&filter[restrictions]=rate) and compares it against the
locally computed values, flagging any drift.