The Holibob API - Look to Book flow
Introduction
This documentation covers the parts of the Holibob API required to discover, configure and book a single product availability.
The sections that follow detail the simplest API calls necessary to:-
Discover Products - You will use the
producList
query along with filters and pagination to present the consumer with products that match their searches.Display Product Details - You will use the
product
query to retrieve more data about a single product that the consumer has chosen to view.Request Availability List - You will use the
availabilityList
query passing a product Id and date range and to discover the dates on which the Product is available to book.Discover and Set Availability Options - You will use the
availability
query passing the ID that relates to the consumer's selected date in order to discover details of any options that the consumer must select and then provide the system with their chosen responses.StartTime: You may be offered different time for the day at which the product is available.
Guide Language: for products that include a guide you may be presented with different languages that they can speak.
A variant of the product: You may be offered different variants of the product such as “Standard” or “Premium”.
… more: there is no hard limit on the number of options for any given product, you must provide a selection for each option.
Discover and Set Availability Pricing Categories We will again use the
availability
query passing the ID to discover and set the number of pax for each price category such as Adult, Child etc.Create a Booking - You will use the
bookingCreate
mutation to create a "cart" so we can add one or more availabilities to this Booking.Add our configured Availability to the Booking - You will use the
bookingAddAvailability
mutation to add the above Availability to the created Booking.OPTIONALLY - You will allow the user to discover further ProductAvailabilities and add these to the same Booking prior to checkout.
Checkout - You will use the
bookingConfirm
mutation to finalise the Booking. At this time the system will confirm the booking with the supplier and, depending on configuration, either we or the system must send confirmation emails and vouchers to the consumer.Retrieve the booking - You can use the
booking
query to request all of the information relating to the finalised booking including any details you may wish to send to the consumer including vouchers, tickets and all related Product details.Retrieve all bookings for ConsumerTrip or Consumer - You can use the
bookingList
query to filter bookings by a specific Consumer Trip or Consumer
It is possible to use the booking
query passing the bookingId to retrieve all information for vouchers and tickets. Be aware however that there is a short period after using the `bookingConfirm is which the booking will be in a PENDING state whilst all checks are completed with the supplier. We will poll the booking query until this state changes
Fetch Product List - The basics
The productList
query is used to obtain a list of Products based on filters and pagination
Each product will have a unique id
that you will use later to retrieve its Availabilities.
Query
Example Responses
In this simple example, we passed a filter for PlaceName="Edinburgh"
and so the system returned a list of products that match that filter.
It is possible to pass many different filters and combine them.
The example asked only for id
, name
and guidePriceFormattedText
and so that is what was returned.
There are however several hundred other attributes of the Product that may have been requested. Note too that guidePrice
and guidePriceCurrency
could have been asked for separately and that the guidePriceFormattedText
is provided only for convenience.
Guide Prices
The guidePrice
will be an APPROXIMATE Adult price for the next nearest availability of the product and is provided only as a guide and a from price. The true price of a product may vary by date and other criteria and will be returned only when a call is made to the availabilityList
query.
Images
Each product will have one o more images available as may be requested with the imageList
property inside of the product nodes. Each image has a unique ID. You can request the image from our global CDN in any format (WebP, PNG, JPG etc) and at any resolution. It is also possible to specify the aspect ration that you will present the image in. The CDN will perform any transformations required and return an image in exactly the resolution, format and shape required by your consumers.
Descriptions
Each product will have a description and may have many other elements of content such as inclusions, exclusions and itinerary. Each content item may be requested in one of nine supported languages including Arabic and may be requested in either PLAIN_TEXT, HTML or JSON format
Fetch Availability List for a given product
Once a consumer has indicated an interest in a given product you will call the availabilityList
query passing the productId
and a filter that includes startDate
and endDate
to determine on what days the product is available. This will return an array of dates that have availability.
In this document we use the format $variableName
to indicate some value that you may be managing in state either as a direct input from the consumer or from the session that the consumer is part of.
Typically these state values will have been determined from the result of a previous interaction with the API.
Query
Example Response
Fetch and respond to Options for a single Availability
Each Availability returned in the previous query has a unique id and relates to a given date in the calendar. Dates that are not returned have zero availability and must not be offered to the consumer. These are cache values and are only guaranteed to live for 24-hours which is typically a lot longer than would ever be required.
You will use the availability
query to both retrieve details of the availability AND update details of the availability.
Using a query to mutate data in a GraphQL API is unusual but not outside of the specification. The pattern is used only in this part of the API as it is significantly easier to work with that using a combination of queries and mutations as is common elsewhere.
You must make iterative calls to the availability
query as you inform the system of choices the consumer has made for
options - returned to you as an array of questions each with an array of possible answers
pricingCategories - returned to you as an array of categories containing details of pricing, restrictions such as
maxAllowed
and interdependencies such as the required ratio of Adult to Child
You are required to present the options to the consumer and submit their responses to the system. Each submission of an option
or pricingCategory
response may result in changes to other options already presented or yet to be discovered.
It is only possible to add the Availability to a Booking once all required options have been submitted and pricingCategory units set
Fetch the initial optionList
Initial Query
Initial Response
Setting the answer for the initial option(s)
In the above, there is a single option requiring the consumer to select either the Premium or Standard tour. You must present this option to the consumer and require a response. When the user has made their choice you will call the query again as below to change the data in the availability.
Answering the first options group
Response informs options are NOT complete
We will see that the response to incomplete
is FALSE and so whilst we did answer all the options that were initially presented we an now assume that there is at least one new option that must be iterated.
In this case we chose a particular version of the Product and so now the system can present us with the startTime options
Repeat the initial call for any further options
Query
Example Response
We will confirm at lines 24 and 25 that our initial option choice has been added to the Availability but observe at line 28 there is a new option for Start Time
We must again therefore submit the response to this option
Setting the answers for any additional options
Query
Example Response
Great! we now receive confirmation that isComplete
= true and so we have satisfied all options.
Of course, this may not have been the case. It is dependent on each product exactly how many times this process may require to be iterated.
Fetch and respond to Pricing Categories for a single Availability
Now that we have confirmation that all options are satisfied it is possible to request the pricingCategory details for the Availability.
Requesting pricingCategories for an Availabilirty that we have not satisfied all options on will always return a null pricingCategory node. This is because the available pricingCategories are dependant on the options
Fetch the Pricing Categories for the Avaialbility
Query
Example Response
We will observe that there are two pricing categories for this option. They are
Adult
Child
Note that the units for each pricingCategory are initially set to zero.
We must now present these pricingCategories to the consumer and allow them to modify the value for units.
There will typically be are also some restrictions on the values that can be set as determined by each of the following
overall
minParticipants - If set, you are required to book at least this number of PAX regardless of their category.
maxParticipants - if set you are required to book no more than this number of PAX regardless of their category.
per category
minParticipants - if set you are required to book at least this number of the given category.
maxParticipants - If set, you are required to book no more that the given number of this category
maxParticipantsDepends - If set, this will indicate a relationship between categories such as the ration of Child to Adult and you must honour this requirement
It is important to ensure that the consumer can not breach these restrictions as doing so will result in an invalid availability that can not be added to a booking.
In order to set the values for units We will make one or more calls to the Availability to set the values.
Setting the units for each requested Pricing Category
Send the units to the availability
Example Response
We will observe that the total gross formatted price has been updated to reflect the units we have set and that the isValid
has returned as true indicating that the options we have requested are valid.
It is important to note that the total price returned may not be the sum and multiplication of the initial per category prices that where initially observed. This is because some products offer volume discounts and so the per category price for certain categories may have been reduced. There are additional fields available on the Availability query that can be used to return this information to the consumer
Creating a booking
Now that you have a valid Availability object you must create a Booking and then add the Availability to this booking before you can begin the checkout flow.
We create a booking with a call to the bookingCreate mutation
Create a new Booking
Mutation
Example Response
We strongly recommend passing the autoFillQuestions = true option to the bookingCreate mutation. Many suppliers will configure optional questions on a product or on the checkout flow and these can present additional friction for the consumer. By using the autoFillQuestions options we are asking the system to bypass these additional questions resulting in less details for the consumer to complete.
The response will contain the unique booking ID just created along with any other state information we have requested.
This booking does not yet include our opinionated availability and so the next step is to add the availability to the booking by making a call to the bookingAddAvailability mutation
Create a new Booking with Consumer Trip
We also support the association of a Consumer Trip on booking with optional supporting fields for both the ConsumerTrip and the Consumer . See below for a basic example
Mutation
Example Response
Add your Availability to your Booking
Request
Example Response
We will observe that isComplete = false
. This will always be the case for a newly created booking and indicates that there are unsatisfied questions on the booking that we must respond to before it is possible to commit the booking
Input answers to the booking
In a process similar to that used to update the Availability you must now:-
Retrieve full details of the booking’s questions
Answer each question that is present
Iterate the process until
isComplete = true
A booking has two optional static values that we recommend you always query and respond to. These are
leadPassengerName - You may set this to any string value. We recommend that you do so in the format “<familyName>, <givenName>” and that you capitalise the <familyName>
partnerExternalReference - You may set this to any string. We recommend that you use this to store your own unique reference for this booking. You may use this value later to retrieve a booking based on your value
In addition there are likely to be one or more booking questions that must be answered. Questions may exist at three different levels within the booking. These are
The Booking - These questions apply to the whole of the booking.
Each Availability - These questions apply to the whole of the availability. As it is possible to add more than one availability to a booking there may be different questions contained in each availability.
Each Person within each Availability - These quesrions relate to each individual added to the given Availability. If say, you have added 2 Adults and one Child to the booking there may be questions for each of the three individuals and each must be presented to the consumer and the answes submitted to the system.
Structure of a question list
Request
Example Response
The following values are queried and returned for each question
id - a unique id for this question that will be used when providing a response
label - the label to be displayed to the consumer. Labels will be returned in the requested language
type - an enumeration for the question type
dataType - the type of data that is expected [TEXT, DATE, OPTIONS etc]
dataformat - the restrictions on the format of the data that must be returned eg [“EMAIL“, “PHONE”]
answerValue - the value of any answer that has already been provided
isRequired - indicates that the question must be satisfied before the booking can be committed
We will now issue a query to retrieve all of the questions for the booking
Fetch the booking questions
Request
Example Response
We will observe in the example response that the questionList contained directly in the single availability node and in each of the person nodes is returned empty in this example but this will not be the case for many products and so it is important to always issue a full query to establish the full list of questions.
We are now in a position to present the nested list of questions to the consumer and seek their responses.
We will then send the consumers responses to the system
Send the Answers for each Booking Question
Request
Example Response
We will confirm that all questions on the booking have been satisfied by requesting the canCommit
value on the response. When this is true
we are able to proceed to commit the booking.
Commit Booking
Our booking is now fully populated with a single opinionatedAvailability and all of the answers to required questions but the booking remains in the OPEN state and has not yet been committed.
The final step in this process is to commit the booking
We must however consider the matter of payment.
Typically, an API partner will be configured for a payment state of “ON_ACCOUNT” and so any booking that has a fully satisfied set of options and questions can immediately be committed. This will cause the system to process the booking and complete the following steps
Send the booking to the supplier
Retrieve any tickets issued by the supplier
Compile a single PDF voucher containing all details of the booking including the embedding of any tickets
Optionally, subject to configuration, send a confirmation email to the Consumer or Booking Agent containing a copy of the voucher
It is assumed that if your API payment is set to ON_ACCOUNT then you will already have arranged to take payment from the consumer for the gross amount of the booking.
When you make a call to the bookingCommit mutation you are confirming your obligation to pay for the Booking and your will be invoices in due course.
To commit a booking we simply make a call to the bookingCommit endpoint passing the id
of the booking.
Commit the booking
Request
Example Response
We will observe that the status of the booking has changed to “PENDING” indicating that the system is performing all necessary steps to communicate with the supplier and compile the voucher. We will also observe that in the initial response, the voucherUrl
is null.
There are several ways in which We will establish that a booking has completed processing. These are
You provide a webhook that Holibob will call upon completion of the processing
You poll the booking until such time as the status changes from “PENDING”
Poll for the state of the Booking
Request
Example Response
Note that for security reasons the voucherUrl is temporary and you should not persist the URL to you storage.
You can of course retrieve the PDF and persist this to storage.
If at any time you need a fresh URL for the voucher you can issue the same query again.
The query is able to return everything that is linked to the Booking, however, in this case you would not want such extensive data.
The query in this example requests the key details of the booking including the following:
code - this is a unique consumer facing code that is equivalent to the ID of the booking and typically used when communicating with customer service representatives.
state - indicates if the booking is OPEN, CONFIRMED, PENDING or FAILED.
voucherUrl - a single use URL that may be used to retrieve the PDF of the voucher. The single PDF will have any tickets embedded within it as may be required by the supplier.
Retrieve all bookings for ConsumerTrip or Consumer
You can identify the consumer and consumerTrip that a booking relates to by using one of the following filters on the bookingList
query
consumerTripExternalReference - filter bookings related to a specific ConsumerTrip reference
consumerExternalReference - filter bookings related to a specific Consumer reference