################### CHANNEL MANAGER API ################### ******************* General information ******************* Hores channel-manager api describes methods and data structures, which may be implemented by external channel-manager or online booking system to write reservations (new, modifications, cancellations) to HORES PMS and to receive rate-codes, availability and restriction updates from HORES PMS. HORES channel-manager interface, which implements this api, is designed as HTTPS client. Availability, rates and restrictions will be pushed to connected server, reservations will be queried from server by periodic requests about new reservations or changes. This architecture was chosen to eliminate need for hotel servers to be accessible from internet, because our software will run on each hotel's servers/computers, it's not centralized/cloud based solution. In future, if needed, we may add own https server as part of the api, so external channel-manager would be able to push reservations directly to HORES PMS when needed. Data will be transferred in JSON format. ************* Configuration ************* Channel-manager interface run's as part of HORES ChannelManager module. When in use, it's necessary to correctly setup "HORES universal channel" in ChannelManager module, same as any other ChannelManager system implemented in this module. Actual channel-manager interface has several options: * **Autostart**: When checked, interface is started automatically when HORES Interface daemon service is started * **Hotel code**: Any string, that will be send with each request from Channel manager interface to target server * **User name**, **Password**: Optional credentials, that will be send with each request in Authorisation header and request body * **Check interval**: Interval in seconds in which reservation changes will be queried from target server * **Reservation check url**: Url to call for reservation-check request * **Reservation confirmation url**: Url to call for reservation-confirmation status for each reservation pulled from reservation-check url * **Rates update url**: Url to send rates and corresponding restrictions * **Availability update url**: Url to send availability and corresponding restrictions *********************************** Authorization, hotel identification *********************************** Interface does not enforce any authorization/other security measures except strict use of https protocol, "HTTP://" urls will not be accepted. If **Hotel code** or "**User name** and **Password** will be set in configuration, those will be part of each request in HTTP header, data or both: User name and password will be send in standard Basic authentication (RFC 2617) = send in Authorisation header, such as .. sourcecode:: http GET /reservations_check_url HTTP/1.1 Host: example.com Accept: application/json Authorization: Basic s5df4h8n4cv5cvv4ert4654h== Both user-name and password will be also part of json request header if set: .. sourcecode:: json { "header": { "user": "sample_username", "password": "sample_password", "hotel_code": "sample_hotel_code" } } ***************** Reservations pull ***************** HORES PMS will periodically send POST request to configured URL to fetch new/modified/canceled reservations: .. http:post:: /check_reservations_url .. sourcecode:: json { "header": { "user": "sample_username", "password": "sample_password", "hotel_code": "sample_hotel_code" }, "task": "get_unprocessed_reservations" } Connected system should respond with list of new/modified/canceled reservations not yet successfully processed by PMS, empty list if there are no such reservations: .. sourcecode:: json [ { "reference_number": "2565", "order_number": "46454dfs5dfd5f4", "status": "new", "source": "Booking.com", "note": "text note with other information", "stays": [ { "id": 45, "date_from": "2018-01-10", "date_to": "2018-01-14", "room_type": "DBL", "rate_code": "BAR", "room_count": 2, "guest_count": 4, "pricing": { "2018-01-10": "100.0", "2018-01-11": "110.0", "2018-01-12": "110.0", "2018-01-13": "110.0" }, "guest_info": [ { "first_name": "Jack", "last_name": "Dunham", "country": "USA", "phone_number": "789456123", "email": "jack@example.com", "address": "Main street 23, NY, USA", "city": "New York", "street": "Main street 23", "postal_code": "56665" }, { } ] } ], "credit_card": { "number": "1254 **** 4856", "ccv": 458, "expire_date": "03/20", "type": "MasterCard" }, "order_contact": { "first_name": "John", "last_name": "Smith", "company_name": "", "country": "USA", "phone_number": "123456789", "email": "john@smith.us", "address": "Main street 23, NY, USA", "city": "New York", "street": "Main street 23", "postal_code": "56665" }, "prepayments": [ { "reference_id": "202300001", "amount": "430.00" }, ] }, { } ] :>json string reservation_number: Unique reservation number provided by channel-manager, MANDATORY :>json string order_number: Reservation number from source system (Booking.com for example) :>json string status: "new", "modified", "cancel", MANDATORY :>json string source: Reservation source (Booking.com, web-booking, etc.) :>json string note: Any other information :>json list stays: Each element of this list represents single room-type reserved for given dates for given price. All fields MANDATORY except "id" field. :>json list guest_info: Each element of this list represents information about single guest for this stay. Number of elements should be less or equal to number of guests in this stay. :>json dict credit_card: You can use full credit-card information (will be encrypted when received) or sanitized number. Valid types: "MasterCard", "AmericanExpress", "DinersClub", "JCB", "Maestro", "Switch", "Visa", "VPay". :>json dict order_contact: Contact information, will be used to fill reservation header. :>json list prepayment: If reservation paid in advance, provide paid amount and optionally refference_id for payment/receipt. Amount should be decimal value represented as string with dot (".") as decimal point, do not use float data-type. You can provide multiple payments if needed, each element corresponds to single transaction. Note about addresses: order_contact and guest_info has "address" field. you can use this if your address is not structured to street, city, postal code etc. If you have structured information available, use respective fields and you can leave "address" field empty. ID field for each "stay" is optional identification of this stay - when multiple "stay" elements are present in single reservation, this identificator may help when modification of such reservation is send to HORES. If ID is not present, HORES will try to match "stays" to internal data by other methods. After PMS will process reservations, confirmation will be set via POST to configured URL. Each reservation will be marked by status = "success" and pms_number if successfully written to PMS, status = "error" and corresponding error text if something went wrong: .. sourcecode:: json { "header": { "user": "sample_username", "password": "sample_password", "hotel_code": "sample_hotel_code" }, "task": "confirm_reservations", "reservations": [ { "reservation_number": "2565", "pms_number": "7889", "opearion": "new", "status": "success" }, { "reservation_number": "2566", "operation": "modify", "status": "error", "error": "Reservation check-in-date in past!", }, { } ] } ****************************************** Room type availability/restrictions update ****************************************** When availability or room-type level restrictions changes, POST request will be send to configured url (keys in "room_types" dictionary are room-type codes): .. sourcecode:: json { "header": { "user": "sample_username", "password": "sample_password", "hotel_code": "sample_hotel_code" }, "task": "update_room_types", "room_types": { "DBL": [ { "date_from": "2018-03-01", "date_to": "2018-03-10", "availability": 5, "closed": false }, { "date_from": "2018-03-11", "date_to": "2018-03-11", "availability": 2, "closed": false }, { } ], "SGL": [ { "date_from": "2018-03-01", "date_to": "2018-03-11", "availability": 10, "closed": true }, { } ] } } Each record in "room_types" dictionary always contains "date_from" and "date_to" fields. "availability" and "closed" fields are optional, depending on what changed one of them may be missing. Target server should reply with: .. sourcecode:: json { "status": "success" } if everything is ok, any reply which will be missing "status": "success" field will be considered as error. In case of error reply can be like this: .. sourcecode:: json { "status": "error", "error": "Error text" } If "error" key is present, it's content will be used to report error. Else whole reply body will be logged as error text. ******************************** Rates prices/restrictions update ******************************** When rate price or restrictions changes, POST request will be send to configured url (keys in "rates" dictionary are rate-codes, each rate-code contains room-type objects): .. sourcecode:: json { "header": { "user": "sample_username", "password": "sample_password", "hotel_code": "sample_hotel_code" }, "task": "update_rates", "rates": { "BAR": { "DBL": [ { "date_from": "2018-03-01", "date_to": "2018-03-10", "price": "200.0", "closed": false, "closed_for_arrival": false, "min_stay": 1 }, { "date_from": "2018-03-11", "date_to": "2018-03-11", "price": "220.0", "closed": false, "closed_for_arrival": false, "min_stay": 1 }, { } ], "TRP": [ { "date_from": "2018-03-01", "date_to": "2018-03-10", "price": "200.0", "closed": false, "closed_for_arrival": false, "min_stay": 1 }, { "date_from": "2018-03-11", "date_to": "2018-03-11", "price": "220.0", "closed": false, "closed_for_arrival": false, "min_stay": 1 }, { } ] }, "BBPLUS": { "DBL": [ { "date_from": "2018-03-01", "date_to": "2018-03-11", "price": "100.0", "closed": true, "closed_for_arrival": true, "min_stay": 3 }, { } ] } } } Each record in "rates" dictionary always contains "date_from" and "date_to" fields. Other fields are optional, depending on what changed. Target server should reply with: .. sourcecode:: json { "status": "success" } if everything is ok, any reply which will be missing "status": "success" field will be considered as error. In case of error reply can be like this: .. sourcecode:: json { "status": "error", "error": "Error text" } If "error" key is present, it's content will be used to report error. Else whole reply body will be logged as error text.