Channel Manager Overview
What this is
Section titled “What this is”The RestroLab Channel Manager — PMS Integration API is a normalized, PMS-agnostic contract for connecting a property management system to the RestroLab Channel Manager for OTA distribution. RestroLab’s own hotel module is the first — and today, only — implementation of this contract: a future external PMS would authenticate the same way and see the same shapes without needing to understand RestroLab’s internal models.
This is a separate, independent API contract from the Third-Party Integration API documented elsewhere on this site — different credentials, different base path (/v1/channel-manager/pms/*), different OpenAPI document, own interactive reference.
Every endpoint requires X-PMS-API-Key / X-PMS-API-Secret authentication and is rate-limited — see Rate limit below.
Endpoints
Section titled “Endpoints”GET /v1/channel-manager/pms/properties/
Section titled “GET /v1/channel-manager/pms/properties/”Returns every property for this credential’s tenant.
[ { "id": "3fae7c1a-2f5b-4b7b-9b2f-1a2b3c4d5e6f", "name": "Hillside Retreat", "address": "12 Ridge Road", "city": "Pokhara", "country_code": "NP" }]| Field | Type | Description |
|---|---|---|
id |
uuid | Property identifier — use this as property_id below. |
name, address, city, country_code |
string |
GET /v1/channel-manager/pms/properties/{property_id}/room-types/
Section titled “GET /v1/channel-manager/pms/properties/{property_id}/room-types/”Room types for a property, including its active rate plans.
[ { "id": "9a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9", "name": "Deluxe King", "max_occupancy": 2, "base_price": "120.00", "rate_plans": [ { "id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f9a0", "name": "Standard", "base_price": "120.00", "is_refundable": true, "min_nights": 1 } ] }]| Field | Type | Description |
|---|---|---|
id |
uuid | Room type identifier — use this as room_type_id on inventory/availability calls. |
name |
string | |
max_occupancy |
integer | |
base_price |
decimal string, nullable | |
rate_plans |
array | Only this room type’s active rate plans. Each has id, name, base_price, is_refundable (boolean), min_nights (integer). |
404 if property_id doesn’t belong to this credential’s tenant.
GET /v1/channel-manager/pms/inventory/
Section titled “GET /v1/channel-manager/pms/inventory/”This PMS’s own current inventory — the same service the front desk uses. Not clamped at 0 — an oversell is reflected, not hidden.
Query parameters (all required): room_type_id (uuid), check_in (YYYY-MM-DD), check_out (YYYY-MM-DD).
curl "https://api.restrolab.com/v1/channel-manager/pms/inventory/?room_type_id=9a1b2c3d-...&check_in=2026-10-01&check_out=2026-10-05" \ -H "X-PMS-API-Key: YOUR_PMS_API_KEY" -H "X-PMS-API-Secret: YOUR_PMS_API_SECRET"[ { "date": "2026-10-01", "physical_capacity": 10, "sellable": 7 }, { "date": "2026-10-02", "physical_capacity": 10, "sellable": 6 }]| Field | Type | Description |
|---|---|---|
date |
date | One entry per night in [check_in, check_out). |
physical_capacity |
integer | Total physical rooms of this type. |
sellable |
integer | Remaining sellable count — can go negative on an oversell, deliberately not clamped to 0. |
404 if room_type_id doesn’t resolve. 400 if check_in/check_out are missing or not valid YYYY-MM-DD dates.
GET /v1/channel-manager/pms/rates/
Section titled “GET /v1/channel-manager/pms/rates/”Read this PMS’s own rate-calendar entries for a rate plan, one entry per night. A date with no explicit override falls back to the rate plan’s base_price, not sold out.
Query parameters (all required): rate_plan_id (uuid), check_in (YYYY-MM-DD), check_out (YYYY-MM-DD).
[ { "date": "2026-10-01", "price": "120.00", "is_sold_out": false }, { "date": "2026-10-02", "price": "135.00", "is_sold_out": false }]404 if rate_plan_id doesn’t resolve. 400 if check_in/check_out are missing or invalid.
PUT /v1/channel-manager/pms/rates/
Section titled “PUT /v1/channel-manager/pms/rates/”Push rate changes into the same table the internal rate-plan UI edits.
Request body:
{ "rate_plan_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f9a0", "rates": [ { "date": "2026-10-01", "price": "140.00", "is_sold_out": false } ]}| Field | Type | Required | Description |
|---|---|---|---|
rate_plan_id |
uuid | yes | |
rates |
array | yes | Each entry: date (required), price (required, decimal), is_sold_out (optional, boolean, default false). |
Response: the same shape as GET /rates/, for every date you just wrote (200 OK).
PUT /v1/channel-manager/pms/restrictions/
Section titled “PUT /v1/channel-manager/pms/restrictions/”PUT-only. Writes stop_sell onto that date’s rate-calendar is_sold_out flag. Writes min_nights onto the whole rate plan’s floor — RestroLab has no per-date minimum-stay override, so the last min_nights value in your request array wins for the entire plan, not just that date.
Request body:
{ "rate_plan_id": "1b2c3d4e-5f60-7182-93a4-b5c6d7e8f9a0", "restrictions": [ { "date": "2026-10-01", "stop_sell": true, "min_nights": 2 } ]}| Field | Type | Required | Description |
|---|---|---|---|
rate_plan_id |
uuid | yes | |
restrictions |
array | yes | Each entry: date (required), stop_sell (optional boolean, default false), min_nights (optional integer, nullable). |
Response: {"updated": <count>} — just a count, not the updated rows.
GET /v1/channel-manager/pms/reservations/
Section titled “GET /v1/channel-manager/pms/reservations/”Reservations for this PMS’s own tenant, newest check-in first, capped at 200 results (no pagination). Optional ?status= query parameter to filter by status.
[ { "id": "5d6e7f80-91a2-43b4-c5d6-e7f8a9b0c1d2", "natural_id": "BKG-000123", "status": "confirmed", "check_in_date": "2026-10-01T14:00:00Z", "check_out_date": "2026-10-05T11:00:00Z", "room_type_id": "9a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9", "total_amount": "480.00", "external_source": "mock_ota", "external_id": "OTA-98765", "guest": { "full_name": "Jane Doe", "phone": "+977...", "email": "jane@example.com" } }]| Field | Type | Description |
|---|---|---|
id |
uuid | RestroLab’s internal booking id. |
natural_id |
string | Human-readable booking reference. |
status |
string | One of pending, confirmed, cancelled, no_show, completed, expired. |
check_in_date, check_out_date |
datetime | |
room_type_id |
uuid, nullable | |
total_amount |
decimal string | |
external_source, external_id |
string, nullable | Set when the reservation originated from an OTA via the Channel Manager. |
guest.full_name |
string | |
guest.phone, guest.email |
string, nullable |
Creating a reservation is not exposed on this API. The create direction always flows OTA → Channel Manager → PMS (via a separate internal webhook receiver), never a PMS pushing a new reservation into the Channel Manager.
GET /v1/channel-manager/pms/reservations/{booking_id}/
Section titled “GET /v1/channel-manager/pms/reservations/{booking_id}/”A single reservation, same shape as above. 404 if booking_id doesn’t belong to this credential’s tenant.
Errors
Section titled “Errors”Errors use the same {"code": ..., "message": ...} envelope as the Third-Party API, but this API is less mature: it does not yet have specific per-case codes like room_type_not_found — 404/400 responses use the generic fallback:
{ "code": "error", "message": "Unknown room_type_id." }{ "code": "invalid_request", "message": "check_in and check_out (YYYY-MM-DD) are required." }| Status | code |
Example message |
|---|---|---|
401 |
invalid_credentials |
"Invalid PMS credentials." |
400 |
invalid_request |
Varies — missing/invalid query params, or array-level validation errors on PUT rates/PUT restrictions. |
404 |
error |
"Unknown room_type_id." / "Unknown rate_plan_id." / "Unknown property." — message text is endpoint-specific. |
429 |
rate_limited |
"Too many requests." |
Rate limit
Section titled “Rate limit”300 requests per minute, per X-PMS-API-Key credential — same mechanism and number as the Third-Party API’s rate limit, confirmed from the backend’s throttle configuration (not yet in the spec). It’s a single shared budget across all /pms/* endpoints, not separate per endpoint.
What’s confirmed vs. TODO
Section titled “What’s confirmed vs. TODO”| Status | |
|---|---|
| Endpoint paths, methods | Confirmed — from openapi.json |
| Request/response field shapes above | Confirmed — from the backend’s views and serializers, not yet in openapi.json |
| Error codes and rate limit | Confirmed — from the backend’s exception handler and throttle config, not yet in openapi.json |
| Authentication | See Authentication |
Explore the reference
Section titled “Explore the reference”The interactive Channel Manager API Reference renders openapi.json directly — it will pick up this same detail automatically once the backend team adds @extend_schema annotations, with no changes needed on this site.
Next steps
Section titled “Next steps”- Authentication — the
X-PMS-API-Key/X-PMS-API-Secretheader scheme this API uses. - Interactive API Reference
