home > support > API > webhooks > Inline Booking Notification
TourCMS Inline booking notification webhooks are a variant on our standard booking notification webhook with the following distinctions:
TourCMS API features a two step booking process.
If a temporary booking is deleted prior to the expiration of the hold time, TourCMS will send you a "temporary_booking_deleted" webhook.
One of the following events:
Please note any events not listed here such as cancellations, redemptions, payments etc do not send as inline synchronous (POST) webhooks and will instead be sent via TourCMS standard asynchronous (GET) webhook.
TourCMS appends the same query string parameters as the standard booking notification webhooks, however as the request is happening inline it will also post the XML output equivalent as if you had called our Show Booking API endpoint.
The XML is signed allowing you to verify that it is sent by TourCMS, see the Verifying the signature section below.
TourCMS will include an X-Request-Id header in the request when sending you the webhook. The value of this header will be a random semi-unique value (currently a UUID4). If you are able to log this header it may help debugging or tracing specific requests.
TourCMS looks for a 200 status code response to indicate a booking success, any other response will be taken as a booking failure.
While your endpoint may return a blank page, if any content is returned by your page it must be valid JSON. Additionally it should be returned with an HTTP Content-type header of application/json.
Booking already committed? Booking already deleted?
These are both endpoints that can be retried in case of failure or indeterminate outcome. Please treat this as a success and return a 200 status, if you are returning a JSON response, consider including a "text" property with text to that effect. We may provide a more structured response for this in future.
For each component* on a booking you may return an array of tickets, these can be your own IDs or IDs from your supplier that you would like to be displayed on standard TourCMS vouchers or vouchers built by OTAs that support them.
You can also return a per-component operator_reference if you have one, for example this could be the booking reference in your system (if this is different to the value returned in the array tickets). The reference does not need to be unique to a given component, for example if you have booked all components on a TourCMS booking as a single booking in your system, all operator_reference would be identical.
You can return a barcode_symbology, by default if you omit this TourCMS will assume "QR_CODE" and while we strongly suggest the use of "QR_CODE" you can alternatively pass "CODE_128".
* Components in TourCMS are individual line items on a booking, for example a booking for 2 Adults, 1 Child on Tour A would give 2 component in TourCMS: one for 2 Adults on Tour A, a second for 1 Child on Tour A. Bookings in TourCMS can contain multiple components from multiple different tours, however generally distributed (OTA) bookings are made for a single tour
Tickets and Operator references beginning with TOURCMS| are not allowed as they are reserved values in TourCMS.
Learn more about vouchers & barcodes in TourCMS
Generally we would strongly recommend that rather than returning your own voucher links that where possible you would return your desired barcode contents (i.e. ticket or booking references mentined above) and allw TourCMS or the OTAs and other API API consumers to generate their own voucher.
However, where that is not possible and where there are particular vouchers that customers must receive you may return an array of urls, either one per component or one per sale_quantity (i.e. per person). If the same URL applies to multiple components (i.e. if a booking for multiple rate types or multiple tours only has a single URL) just return that same URL against each component it relates to, the OTA / API consumer can de-dupe and display as they wish.
Each url must be of type "voucher", have a valid link and mime_type (currently only you may return an array of text/html and application/pdf are supported).
TourCMS standard booking hold time is 45 minutes†, this means that an API consumer making a successful call to the first step of our two step booking process would expect that booking to be held for 45 minutes, and for a subsequent "Commit" call to be successful within that time.
TourCMS will send you the expected hold time in seconds as hold_time_seconds node during the first inline webhook, we would strongly recommend you match the requested hold time where possible as not doing so may limit which OTAs you can sell tours via. If you omit to return a hold_time_seconds TourCMS will assume the requested hold time. A hold_time_seconds less than 300 (5 minutes) will be ignored. A hold_time_seconds returned during step two ("Commit") will have no effect.
† There are some variances, for example Get Your Guide mandate a 1 hour hold time (3600 seconds).
If you provide a text property with your response TourCMS will return this alongside any error message, for example if there is a problem creating a temporary booking you should output the reason why as a "text". Use of this field is strongly recommended.
Remember, to merely accept the inline webhook you may return a simple empty 200 HTTP response. However a JSON response showing the status can be useful for logging purposes:
{
"hold_time_seconds": 2700,
"text": "Booking accepted"
}
{
"hold_time_seconds": 2700,
"text": "",
"components": [{
"component_id": "1234",
"barcode_symbology": "QR_CODE",
"operator_reference": "1234567",
"tickets": [{
"label": "Adult entry",
"value": "1234568"
},
{
"label": "Adult entry",
"value": "1234569"
}
]
},
{
"component_id": "1235",
"barcode_symbology": "QR_CODE",
"operator_reference": "1234567",
"tickets": [{
"label": "Child entry",
"value": "1234570"
},
{
"label": "Child entry",
"value": "1234571"
}
]
}
]
}
{
"hold_time_seconds": 2700,
"text": "",
"components": [{
"component_id": "1234",
"urls": [{
"link": "http://www.example.com/1234568",
"type": "voucher",
"label": "Adult entry",
"mime_type": "text/html",
},
{
"link": "http://www.example.com/1234569",
"type": "voucher",
"label": "Adult entry",
"mime_type": "text/html",
}
]
},
{
"component_id": "1235",
"urls": [{
"link": "http://www.example.com/1234570",
"type": "voucher",
"label": "Child entry",
"mime_type": "text/html",
},
{
"link": "http://www.example.com/1234571",
"type": "voucher",
"label": "Child entry",
"mime_type": "text/html",
}
]
}
]
}
Inline booking notifications are not provided as standard functionality in TourCMS, please contact us to discuss enabling the feature on your account. Once access has been enabled you can switch the webhooks on/off via the standard configuration page in Configuration & Setup > Webhooks.
TourCMS will post to the same URL as configured for the standard booking notification webhooks in Configuration & Setup > Webhooks.
The inline webhooks respect the same channel restrictions as the standard webhooks, configured via Configuration & Setup > Webhooks, additionally exclusive to the inline webhooks it is possible to whitelist/blacklist individual tours.
If you are placing a booking in your own TourCMS account for testing purposes, or to price up only you can skip the temporary inline booking webhook by passing <skip_inline_webhook>1</skip_inline_webhook> in your call to "Start new booking".
TourCMS adds a signed hash the booking XML prior to posting it, you can verify the hash by concatenating the XML node values specified in the hash_fields with a pipe character inbetween each value, then hashing using the algorithm specified in the algorithm node using your TourCMS API key.
The following is an example of the signed XML node:
<response>
...
<booking>
...
</booking>
<signed>
<hash_fields>/response/booking/booking_id /response/booking/channel_id /response/booking/made_date_time /response/booking/components/component/component_id /response/booking/components/component/sale_quantity</hash_fields>
<algorithm>sha256</algorithm>
<hash>9a08bc4be09b94a66879820d51dd044d21c148cd45acace77ce6e47188d131da</hash>
</signed>
...
</response>
The following is a reference implementation in PHP, adapted from our PHP library:
function validate_xml_hash($xml, $private_key) {
// Get an array of XPath queries
$fields = explode(" ", $xml->signed->hash_fields);
// Loop through each query
foreach($fields as $field) {
// Run the XPath query
$xpath_result = $xml->xpath($field);
// Loop through each result and build an array of node values
foreach($xpath_result as $result) {
$values[] = (string)$result[0];
}
}
// Generate the string to sign by concatenating the values with a pipe
$string_to_hash = implode("|", $values);
// Get the algorithm to use from the XML
$algorithm = $xml->signed->algorithm;
// Generate the hash
$hash = hash_hmac($algorithm, $string_to_hash, $private_key, FALSE);
// Compare the hash with the one provided in the XML
return $hash == $xml->signed->hash;
}
If the functionality has been enabled for your account by Palisis staff you can test your integration without enabling the webhooks by passing <force_inline_webhook>1</force_inline_webhook> to Start new booking, Commit booking or Delete booking. This flag tells TourCMS to send the webhook regardless of whether you have turned inline webhooks on, regardless of whether the booking matches the whitelist/blacklist of tours and in the case of Commit booking regardless of whether the booking is about to go confirmed.
In addition to skipping the above checks, passing this parameter returns additional information regarding the response of your webhook endpoint which can help with debugging:
<webhook>
<webhook_response>Any content received from your webhook endpoint</webhook_response>
<webhook_roundtrip>0.011</webhook_roundtrip>
<webhook_http_status>200</webhook_http_status>
<webhook_url>The URL TourCMS called</webhook_url>
<webhook_payload>The XML payload TourCMS sent</webhook_payload>
</webhook>
This extra information can also be returned without forcing the webhook by passing <webhook_info>1</webhook_info>.
This page details a work in progress specification, the following changes have been made: