Skip to main content

Channel Manager (Channex)

The Channex integration distributes our catalogue to OTAs 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 bookings back.

It lives in the admin-api module under com.elivaas.pms and is driven by manual admin endpoints.

Entity Mapping

Our entityChannex 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 BookingStored raw and acknowledged

A single listing becomes one Channex property; each of the listing's properties becomes a room type with a primary rate plan underneath it.

How It Works

Admin onboards a listing (channelId=chnl_X)

ChannexSyncService.onboardListing(listingId, channelId)
│ Resolve per-channel credentials from channels.channel_config

├─ Create/update Channex PROPERTY ← Listing
│ store channex_property.channex_property_id

├─ For each property:
│ Create/update ROOM TYPE ← Property
│ Create/update RATE PLAN (primary)
│ store channex_room_type.{room_type_id, rate_plan_id}

└─ Register a booking WEBHOOK (idempotent)
store channex_property.channex_webhook_id

Admin pushes ARI

ChannexSyncService.pushAri(listingId, channelId, from, to)
├─ Inventory → POST /availability (keyed by room_type_id)
└─ ChannelRate → POST /restrictions (keyed by rate_plan_id)

Channex sends a booking

POST /api/channex/webhook?channelId=chnl_X
└─ ChannexBookingService: fetch full booking → persist raw → ack

Stored Mappings

Channex UUIDs are persisted so subsequent syncs update in place rather than creating duplicates. Three tables back the integration (migration 047-add-channex-tables.sql):

TableGrainHolds
channex_propertyone row per onboarded listingchannex_property_id, channex_webhook_id, status, last error/sync
channex_room_typeone row per propertychannex_room_type_id, channex_rate_plan_id, status
channex_bookingone row per inbound bookingraw payload, status (RECEIVED/ACKED/FAILED)

Each mapping row carries a status (PENDING / SYNCED / FAILED) and a 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:

FieldMeaning
channexApiKeyAccount API key, sent as the user-api-key header
channexEnvironmentSTAGING (default) → staging.channex.io, PRODUCTIONsecure.channex.io
channexGroupIdChannex group UUID a created property is associated with
channexWebhookSecretOptional shared secret for verifying inbound webhooks

The webhook callback host is set with the channex.webhook.base-url application property; the registered callback URL embeds ?channelId= so the receiver can resolve the right credentials.

See Catalogue Mapping, Availability & Rates, and Bookings & Webhooks.