Channel Partner API

Push rooms, rates and availability from your system. Receive bookings back in real time.

v1.0Updated 2026-08-22
Base URL: https://api.glacialadventures.in/apiV1/channel/v1/your_source_code/

your_source_code stands in for the source code Glacial assigns you at onboarding. Requests to any other host will not reach the gateway.

Overview

The Channel Partner API is a two-way interface between Glacial Adventures and channel managers, property management systems and central reservation systems. Your system pushes rooms, rates and availability to Glacial; Glacial delivers confirmed bookings, modifications and cancellations back to your system in real time.

Your system
Channel manager, PMS or CRS
Rates · Availability
Bookings · Cancellations
Glacial channel gateway
/api/channel/v1/
Your system pushes rates, availability and no-show reports to the Glacial channel gateway; the gateway pushes bookings, changes and cancellations back to your system.

Inbound, you call four endpoints: room and rate-plan discovery, rate push, availability push, and no-show reporting. Outbound, Glacial POSTs booking events to an HTTPS endpoint you register with us.

Every inbound request is an HTTP POST with a JSON body and a Content-Type of application/json. There are no GET endpoints and no query parameters. The trailing slash on each path is optional.

About the examples on this page

Every request below is copy-pasteable and runs as written. your_source_code, GLH101, R55 and secret_token_value are placeholders — substitute the values Glacial issues you. Run an example unchanged and you will get a well-formed error envelope back, which is the quickest way to confirm you can reach the gateway.

Getting connected

Credentials are issued by Glacial. There is no self-service signup and no sandbox key you can mint yourself — every integration starts with an application and ends with a certification pass.

  1. 1
    Apply

    Submit the Become-a-Partner form below. It's the only queue.

  2. 2
    We review

    Our partnerships team replies within 2 working days, on WhatsApp or email.

  3. 3
    Your partner account

    Approved partners receive an email invite. Set your password and sign in to the partner extranet.

  4. 4
    Credentials & integration

    Your test-property hotel code and AuthCode are on your API Integration page, viewable whenever you need them. Integrate the flows in this documentation against the base URL shown above; your credentials identify you in the request body — there is nothing partner-specific in the URLs.

  5. 5
    Certification & go live

    We verify rate, availability and booking flows together on your test property, then enable production pushes across your hotels. Endpoints, payloads and credentials do not change between test and production.

How credentials work

Two credentials, two channels.

Your sign-in is a personal account — we email you an activation link when you're approved, and you choose your own password. Your API credentials (hotel code + AuthCode) are never emailed and never sent over WhatsApp: they live on the API Integration page of your partner account — masked by default, viewable by you whenever you need them, with every reveal logged. Nobody from Glacial will ever ask you for your AuthCode. If a code is ever compromised, one message to us rotates it instantly without affecting any other property.

Authentication

Inbound requests authenticate in the JSON body, not in headers. Every request carries an auth block with the property's hotel code and API key. There is no Authorization header, no signature and no OAuth exchange on the inbound leg.

auth block
{
  "auth": {
    "hotel_code": "GLH101",
    "api_key": "secret_token_value"
  }
}

The source code in the URL identifies you as a partner; the auth block identifies the property. Both are checked on every request, and a valid key for one property will not authenticate another.

AUTH_FAILED does not say which half is wrong

An unknown hotel code, an inactive property and an incorrect API key all return the same AUTH_FAILED code. This is deliberate — the response must not confirm which property codes exist. When you get AUTH_FAILED, check both values against what was issued rather than assuming the key is at fault.

API keys are shown once, at issue. Glacial does not store a form you can read back, so keep the key in your own secret store. Keys can be rotated on request; the previous key stops working the moment the new one is issued, so schedule a rotation for a quiet window.

Outbound booking delivery authenticates the other way round. Glacial sends a bearer token you nominate, in a standard Authorization header, so you can verify that a booking event genuinely came from us.

Outbound request headers
Authorization: Bearer <your_outbound_token>
Content-Type: application/json
Accept: application/json

Identifiers

Three identifier formats appear across the API. All of them are case-sensitive, and all of them are assigned by Glacial — never construct one yourself.

IdentifierFormatExampleWhere it appears
Hotel codeGLH<id>GLH101auth.hotel_code, and hotel_code on outbound events
Room codeR<id>R55room_code on rate and inventory rows
Rate plan codeR<id>_<MEAL>R55_CPrate_plan_code on rate rows

The meal component of a rate plan code is one of five values, always upper case:

Meal codePlanIncluded
EPEuropean PlanRoom only
CPContinental PlanRoom and breakfast
MAPModified American PlanRoom, breakfast and one further meal
APAmerican PlanRoom and all meals
JPJain PlanRoom with Jain meals
/roominfo/ is the only source of truth

A property does not necessarily offer every meal plan, and room codes change when a property reconfigures its inventory. Push a rate plan a room does not offer and the row fails with INVALID_RATE_PLAN even though the code is well-formed. Call /roominfo/ to read the live set, cache the result, and refresh it on a schedule rather than deriving codes from a room id.

Bookings carry a reservation reference in the form GL-<booking>-<property>, for example GL-2026-001-H101. Treat it as an opaque string: it is the value you echo back in /noshow/ and the key you store against your own booking record.

Conventions

Every inbound response is HTTP 200, including failures. The body carries the outcome in a fixed envelope; branch on success, never on the HTTP status. A non-200 status means the request never reached the gateway — a proxy, a network fault or a malformed body — and should be retried rather than parsed.

Response envelope
{
  "success": false,
  "code": "AUTH_FAILED",
  "message": "invalid api_key"
}

On success, code is an empty string and message is a human-readable summary. Discovery adds a rooms array beside the envelope; the batch endpoints add nothing.

Rate and inventory pushes are batches, and a batch is not all-or-nothing. Rows are applied independently: valid rows are written, invalid rows are itemised in the message by their zero-based index in the array you sent. The batch reports success as long as at least one row applied, so a 200 with success true still deserves a look at message.

Partial batch — first row applied, third row rejected
{
  "success": true,
  "code": "",
  "message": "2 rows applied; 14 dates; 1 rows failed: #2 INVALID_ROOM"
}

When every row fails, success is false and code carries the first failing row's code. Because indexes are zero-based, #2 is the third element of the array.

Dates are YYYY-MM-DD and every range is inclusive of both ends: start_date 2026-09-01 with end_date 2026-09-07 writes seven nights. A range whose end_date precedes its start_date fails the row with INVALID_RANGE.

Dates in the past are skipped, not failed. A range that straddles today writes the future half, reports the skipped count in message, and still counts as applied. A range entirely in the past writes nothing and applies no row — which is a success with zero rows applied, not an error.

A single row may span at most 400 days; a longer range fails that row with RANGE_TOO_LONG. There is no hard cap on rows per request, but keep a request to roughly 500 written dates in total — rows multiplied by nights — and split larger updates across several calls. Long single ranges are cheaper than many one-night rows covering the same period.

Null means unspecified, not zero

Omitting an optional field, or sending null, leaves the stored value untouched — the previous value survives. Sending 0 sets the value to zero. Omitting extra_adult_amount on an update does not clear an existing extra-adult surcharge; to remove one, send 0 explicitly.

A rate push alone does not open a room for sale

When a rate lands on a date that has no inventory record yet, that date is created closed — zero rooms, stop-sale on. Rates set the price; availability comes only from /inventory/. An integration that pushes rates and never pushes inventory produces a property that is fully priced and entirely unbookable, with no error to signal it.

Rate rows may carry an ota_code to target a single channel. Leave it null or omit it and the rate applies to every channel — this is what almost every integration wants. Send a value and the row is applied only if it matches the OTA code assigned to your link; a row naming any other channel is silently skipped and counted in message as foreign-OTA skipped, not reported as a failure.

Amounts are in INR and are per room, per night, for the room's default occupancy. Extra adult and child amounts are surcharges on top of that base, not replacements for it.

Room and rate plan discovery

POST/channel/v1/your_source_code/roominfo/

Returns the active rooms for a property and the rate plans each one supports. Call this first, and call it again whenever a push starts failing with INVALID_ROOM or INVALID_RATE_PLAN — it is the mapping you build every other request against.

Request body

authObject. Present on every inbound request.
FieldTypeDescription
hotel_code *stringThe property identifier Glacial issued for this link, e.g. GLH101.
api_key *stringThe secret key issued alongside the hotel code.
curl -X POST 'https://api.glacialadventures.in/apiV1/channel/v1/your_source_code/roominfo/' \
  -H 'Content-Type: application/json' \
  -d '{
  "auth": {
    "hotel_code": "GLH101",
    "api_key": "secret_token_value"
  }
}'
Response · HTTP 200
{
  "success": true,
  "code": "",
  "message": "Success",
  "rooms": [
    {
      "room_code": "R55",
      "room_name": "Deluxe Room",
      "rate_plans": [
        {
          "code": "R55_EP",
          "name": "Deluxe Room EP"
        },
        {
          "code": "R55_CP",
          "name": "Deluxe Room CP"
        },
        {
          "code": "R55_MAP",
          "name": "Deluxe Room MAP"
        },
        {
          "code": "R55_AP",
          "name": "Deluxe Room AP"
        }
      ]
    }
  ]
}
  • The rooms array is always present, including on failures, where it is empty — you can read it without branching on success first.
  • Only rooms currently active on the property are returned. A room that disappears between calls has been removed or deactivated; stop pushing to it.
  • Discovery is not affected by a paused push link: it answers even when rate and inventory pushes are disabled.

Rate push

POST/channel/v1/your_source_code/rates/

Sets nightly rates for a room and rate plan across a date range, with optional extra adult and extra child surcharges. Send one row per room, rate plan and contiguous range.

Request body

authObject. Present on every inbound request.
FieldTypeDescription
hotel_code *stringThe property identifier Glacial issued for this link, e.g. GLH101.
api_key *stringThe secret key issued alongside the hotel code.
rates[]Array of rate rows. Each row is applied independently.
FieldTypeDescription
room_code *stringRoom code from /roominfo/, e.g. R55.
rate_plan_code *stringRate plan code from /roominfo/, e.g. R55_CP. Must belong to room_code and be a plan the room actually offers.
amount *numberNightly rate in INR for the room's default occupancy.
start_date *string (YYYY-MM-DD)First night the rate applies to, inclusive.
end_date *string (YYYY-MM-DD)Last night the rate applies to, inclusive. At most 400 days after start_date.
currencystringAccepted for readability; amounts are settled in INR regardless.
ota_codestring | nullRestricts the row to one channel. Null or omitted applies it to all channels — the usual case.
extra_adult_amountnumber | nullSurcharge per extra adult. Null or omitted leaves the stored value unchanged; send 0 to clear it.
extra_child_amountnumber | nullSurcharge per extra child. Null or omitted leaves the stored value unchanged; send 0 to clear it.
curl -X POST 'https://api.glacialadventures.in/apiV1/channel/v1/your_source_code/rates/' \
  -H 'Content-Type: application/json' \
  -d '{
  "auth": {
    "hotel_code": "GLH101",
    "api_key": "secret_token_value"
  },
  "rates": [
    {
      "room_code": "R55",
      "rate_plan_code": "R55_CP",
      "amount": 4500,
      "currency": "INR",
      "start_date": "2026-09-01",
      "end_date": "2026-09-07",
      "ota_code": null,
      "extra_adult_amount": 800,
      "extra_child_amount": 500
    }
  ]
}'
Response · HTTP 200
{
  "success": true,
  "code": "",
  "message": "1 rows applied; 7 dates"
}
Pair every rate push with an inventory push

Dates created by a rate push start closed. Until /inventory/ sets a room count for the same dates, the rate is stored but the room cannot be booked.

  • A row is rejected with INVALID_RATE_PLAN if the plan does not belong to the room or the room does not offer that meal plan, even when the code is correctly formed.
  • Rows targeting another channel's OTA code are skipped silently and counted in message — they are not failures.
  • Re-sending a row overwrites the rate for those dates. Pushes are idempotent, so a full refresh is a safe recovery from a missed update.

Availability push

POST/channel/v1/your_source_code/inventory/

Sets the number of rooms available to sell across a date range. This is what opens a room for sale, and it is also how you close one.

Request body

authObject. Present on every inbound request.
FieldTypeDescription
hotel_code *stringThe property identifier Glacial issued for this link, e.g. GLH101.
api_key *stringThe secret key issued alongside the hotel code.
inventory[]Array of availability rows. Each row is applied independently.
FieldTypeDescription
room_code *stringRoom code from /roominfo/, e.g. R55.
available_rooms *integerRooms available to sell per night. Must be zero or greater; 0 applies a stop-sale.
start_date *string (YYYY-MM-DD)First night of the range, inclusive.
end_date *string (YYYY-MM-DD)Last night of the range, inclusive. At most 400 days after start_date.
curl -X POST 'https://api.glacialadventures.in/apiV1/channel/v1/your_source_code/inventory/' \
  -H 'Content-Type: application/json' \
  -d '{
  "auth": {
    "hotel_code": "GLH101",
    "api_key": "secret_token_value"
  },
  "inventory": [
    {
      "room_code": "R55",
      "available_rooms": 8,
      "start_date": "2026-09-01",
      "end_date": "2026-09-07"
    },
    {
      "room_code": "R55",
      "available_rooms": 0,
      "start_date": "2026-09-08",
      "end_date": "2026-09-08"
    }
  ]
}'
Response · HTTP 200
{
  "success": true,
  "code": "",
  "message": "2 rows applied; 8 dates"
}
  • available_rooms is an absolute count, not a delta. Send the number you want on sale, not the change since the last push.
  • Sending 0 closes the room for those dates. Sending a positive count again reopens it — there is no separate stop-sale endpoint.
  • A negative or non-numeric count fails the row with BAD_COUNT.
  • Availability is per room, not per rate plan. All of a room's rate plans draw on the same pool.

No-show reporting

POST/channel/v1/your_source_code/noshow/

Reports that a guest did not arrive. Marks the reservation as a no-show on the Glacial side and notifies the reservations team.

Request body

authObject. Present on every inbound request.
FieldTypeDescription
hotel_code *stringThe property identifier Glacial issued for this link, e.g. GLH101.
api_key *stringThe secret key issued alongside the hotel code.
Body
FieldTypeDescription
reservation_ref *stringThe reservation reference from the booking event, e.g. GL-2026-001-H101. Your own booking id is accepted here too, if you returned one when the booking was delivered.
curl -X POST 'https://api.glacialadventures.in/apiV1/channel/v1/your_source_code/noshow/' \
  -H 'Content-Type: application/json' \
  -d '{
  "auth": {
    "hotel_code": "GLH101",
    "api_key": "secret_token_value"
  },
  "reservation_ref": "GL-2026-001-H101"
}'
Response · HTTP 200
{
  "success": true,
  "code": "",
  "message": "reservation marked no_show"
}
  • The reference must belong to the property in the auth block. A reference from another property returns NOT_FOUND.
  • The call is idempotent: reporting a reservation that is already marked no-show succeeds and does not raise a second notification.
  • Reporting a no-show does not cancel the booking or trigger a refund. Commercial resolution is handled by the reservations team.

Outbound booking delivery

When a booking is confirmed, changed or cancelled at Glacial, we POST a JSON event to the bookings endpoint you register during onboarding. The endpoint must be HTTPS and reachable from the public internet.

Each request carries your bearer token in an Authorization header and expects a JSON acknowledgement. Requests time out after ten seconds by default.

Booking event — event: create
{
  "event": "create",
  "reservation_ref": "GL-2026-001-H101",
  "hotel_code": "GLH101",
  "sent_at": "2026-09-01T10:15:00+05:30",
  "booking": {
    "reference": "GL-2026-001",
    "created_at": "2026-09-01T10:12:00+05:30",
    "guest": {
      "given_name": "Asha",
      "surname": "Menon",
      "salutation": "Ms",
      "phone": "+910000000000",
      "email": "guest@example.com"
    },
    "comments": [
      "Late check-in requested",
      "Channel booking GL-2026-001"
    ]
  },
  "stays": [
    {
      "stay_ref": "5001",
      "room_code": "R55",
      "room_name": "Deluxe Room",
      "rate_plan_code": "R55_CP",
      "rate_plan_name": "Deluxe Room CP",
      "rooms": 1,
      "checkin": "2026-09-01",
      "checkout": "2026-09-05",
      "guests": {
        "adults": 2,
        "children": 1,
        "infants": 0
      },
      "amount": "18000.00",
      "currency": "INR",
      "resolution": "hint"
    }
  ],
  "payment": {
    "status": "prepaid",
    "paid_amount": "18000.00",
    "total_amount": "18000.00"
  }
}
eventMeaningPayload notes
createA new reservation has been confirmed.Full payload with at least one stay.
modifyDates, rooms, occupancy, amounts or guest details changed.Full current state, not a diff. Replace what you hold against reservation_ref.
cancelThe reservation has been cancelled.stays is empty; reservation_ref identifies what to cancel.

reservation_ref is stable across all three events for a given reservation, so it is the key to store and match on. sent_at and event are stamped at delivery time; everything else describes the reservation's current state.

No card data is ever sent. The payment block reports what has been collected and what is owed, nothing more.

To acknowledge an event, return HTTP 200 or 201 with a JSON body containing success true and your own reference for the booking:

Expected acknowledgement
{
  "success": true,
  "booking_id": "PMS-REF-789012"
}
A response without booking_id is treated as a failure

All three of a 2xx status, success: true, and a non-empty booking_id are required. A 200 carrying only success: true is recorded as a failed delivery and the create is retried — which, in a system that stores the booking on receipt, produces duplicates. Return the id your system assigned, on every event including modify and cancel. If you have no natural id, echo reservation_ref back.

To reject a booking your system genuinely cannot accept — an unmapped room category, a closed property — return HTTP 400 or 422 with success false and a message. The event is recorded as BOOKING_REJECTED for the operations team to resolve, and is not retried.

Business rejection
{
  "success": false,
  "message": "Room category R55 is not mapped in the PMS"
}

Transport failures and the statuses 429, 500, 502, 503 and 504 are retried up to three attempts with an increasing delay, honouring Retry-After when you send one. Anything else — a non-JSON body, a missing booking_id, another 4xx — is recorded as a failed delivery and surfaced to the operations team rather than retried automatically.

Make your endpoint idempotent

Because retries exist, the same event can arrive more than once. Key on reservation_ref and treat a repeat as an update to what you already hold. Acknowledge a duplicate with the same booking_id and HTTP 200 rather than an error.

Error codes

Inbound codes arrive in the code field of an HTTP 200 envelope. The outbound code describes how Glacial records a booking your endpoint refused.

CodeLegCauseResolution
AUTH_FAILEDInboundThe auth block is missing, the hotel code is unknown, the property is inactive, or the API key does not match. The response does not distinguish between these.Check hotel_code and api_key against what was issued for this property. If both look right, the property link may have been deactivated — contact partners@glacialadventures.in.
UNKNOWN_PARTNERInboundThe source code in the URL is not recognised, or the partner record is not active.Use the source code Glacial issued you, lower case, exactly as it appears in your onboarding email. This is a URL problem, not a credential problem.
PARTNER_PAUSEDInboundInbound pushes are paused for your integration as a whole, across every property.Stop pushing and contact Glacial. A pause is set deliberately by the operations team; retrying will not clear it.
PUSH_DISABLEDInboundPushes are disabled for this one property link, while your other properties continue to work.The property is likely being reconfigured or has moved off channel management. Confirm with Glacial before resuming pushes for it. Discovery still works.
CONFIG_GUARDInboundThe gateway declined to serve the request because it was not reached through the correct production host.Send requests to https://api.glacialadventures.in. If you are already using that host, report the failure to Glacial — no retry will succeed until it is fixed on our side.
INVALID_ROOMInboundA row's room_code does not correspond to an active room on the property.Call /roominfo/ and refresh your mapping. The room has been renamed, removed or deactivated since you last read it.
INVALID_RATE_PLANInboundA row's rate_plan_code is malformed, belongs to a different room, or names a meal plan this room does not offer.Use a rate plan code exactly as returned by /roominfo/ for that room. A well-formed code such as R55_AP still fails if room R55 does not sell AP.
BAD_RATEInboundA rate row's amount is missing, non-numeric or unparseable.Send amount as a JSON number, not a formatted string. Strip currency symbols and thousands separators before sending.
BAD_DATEInboundstart_date or end_date is missing, or is not a valid YYYY-MM-DD date.Send plain calendar dates with no time component and no timezone suffix. 2026-09-01 is valid; 01/09/2026 and 2026-09-01T00:00:00Z are not.
INVALID_RANGEInboundend_date falls before start_date.Ranges are inclusive of both ends. For a single night, set both fields to the same date.
RANGE_TOO_LONGInboundA single row spans more than 400 days.Split the row into consecutive ranges of 400 days or fewer. The limit is per row, not per request.
BAD_COUNTInboundavailable_rooms is missing, negative or not a number.Send a whole number of zero or more. Use 0 to close the room rather than a negative value or null.
NOT_FOUNDInboundThe reservation_ref sent to /noshow/ does not match any reservation on this property.Use the reservation_ref from the booking event, or the booking id you returned when acknowledging it. References are scoped to the property in the auth block.
BAD_REQUESTInboundThe body could not be processed — an array element that is not an object, a missing rates or inventory array, or an unexpected error while applying the batch.Validate the payload against the examples on this page. Check that rates and inventory are arrays of objects and that Content-Type is application/json.
BOOKING_REJECTEDOutboundYour endpoint answered a booking event with HTTP 400 or 422, or with success false — a business rejection rather than a transport fault.Nothing to do on the wire; the event is not retried. Glacial's operations team follows up on the reason you returned in message, so make it specific.

Changelog

  1. v1.02026-08-22
    • Initial public release of the Channel Partner API reference.

Contact

The application form is how integrations start — there is no separate queue. Already a partner? Your account manager and partners@glacialadventures.in are your channels, and WhatsApp is fastest.