Channel Manager (Channex)
The Channex integration distributes our catalogue to OTAs (Booking.com, Airbnb, Expedia, …) through the Channex channel manager. Unlike the Staah and Livbnb integrations — which pull rates and inventory into our database — Channex is a push integration: we mirror our catalogue up into Channex, push availability and rates, and receive OTA bookings back as real PMS bookings.
It lives in the admin-api module under com.elivaas.pms.
Entity Mapping
| Our entity | Channex entity |
|---|---|
Listing (lst_…) | Property |
Property (prop_…) | Room Type (+ one primary Rate Plan) |
Inventory (per property/date) | Availability (ARI) |
ChannelRate (per property/date) | Restriction / rate (ARI) |
| Channex booking revision | A real PMS bookings row (OTA-sourced) |
A single listing becomes one Channex property; each of the listing's properties becomes a room type with a primary rate plan underneath it.
What it does
- Catalogue mapping — onboard a listing → property, room types, rate plans, and a booking webhook (idempotent).
- Availability & rates — push ARI, event-driven and debounced off our
inventory.changed/rate.changedevents, range-compressed, netting cart holds, with an hourly drift correction. - Bookings — receive OTA bookings via webhook and a feed-poll backstop, and map each into a real PMS booking (inventory decremented, money reverse-derived).
- Channel API — connect an OTA (e.g. Booking.com) programmatically: test connection, read its rooms/rates, map them, activate.
- Airbnb — the OAuth-based Airbnb connection flow, per-listing mappings, settings and promotions.
- Doctor / health check — verify the integration end-to-end (key → API → mappings → ARI read-back → feed).
How It Works
Onboard a listing (channelId=chnl_X) ── ChannexSyncService.onboardListing
├─ Create/update Channex PROPERTY ← Listing → channex_property
├─ For each property: ROOM TYPE + RATE PLAN ← Property → channex_room_type
└─ Register a booking WEBHOOK (idempotent) → channex_property.channex_webhook_id
Inventory / rate change (the normal path)
inventory.changed / rate.changed (SNS) → SQS → ChannexAriEventConsumer
→ debounce → push only the touched cells (availability + restrictions)
+ hourly drift job: full ARI push for every onboarded listing
OTA booking
webhook (revision_id) ── and ── feed poll (backstop, every 15 min)
→ GET /booking_revisions/{id} → store raw + ACK the revision
→ ChannexOtaBookingService: create/cancel a real PMS booking, adjust inventory
Stored Mappings
Channex UUIDs are persisted so subsequent syncs update in place (migration 047-add-channex-tables.sql):
| Table | Grain | Holds |
|---|---|---|
channex_property | one row per onboarded listing | channex_property_id, channex_webhook_id, status |
channex_room_type | one row per property | channex_room_type_id, channex_rate_plan_id, status |
channex_booking | one row per inbound booking | channex_revision_id, raw payload, status (RECEIVED/ACKED/FAILED) |
Each mapping row carries a status (PENDING / SYNCED / FAILED) and last_error, so a partial sync
is visible per entity.
Configuration
Credentials are stored per channel in the channels.channel_config JSONB column, so multiple Channex
accounts and environments can coexist:
| Field | Meaning |
|---|---|
channexApiKey | Account API key, sent as the user-api-key header |
channexEnvironment | STAGING (default) → staging.channex.io, PRODUCTION → secure.channex.io |
channexGroupId | Channex group UUID a created property/channel is associated with |
channexWebhookSecret | Optional shared secret for verifying inbound webhooks |
Application properties (feed poll, SQS, debounce, drift, booking auto-apply) are covered in Deployment & Config.