Viator API Certification

Jun 10, 2021 | Merchant API, Travel Commerce Partners

This page is your complete guide to Viator’s API certification requirements, including instructions and best practices for each element we certify.

Please note that this resource focuses on the elements required to pass certification. If you are looking for more detailed information on a particular concept or endpoint, please refer to our technical documentation.

Who is this guide for?

This page is intended for API partners who are using the Viator Partner API.

As a Viator Partner, you use Viator’s API to display and book Viator’s tours and activities.

The certification requirements listed here apply to the following solutions described below:

  • If your organization is the merchant of record, you are responsible for all transactions carried out by your customers, as well as post booking processes and customer support.
  • If you are a full + booking access affiliate, you are responsible for sharing customer’s payment information with Viator in a PCI-compliant way, in order for Viator to process the payment. Viator is the merchant of record and Viator will handle any communication about bookings as well as cancellations and refunds.

Why do we certify API implementations?

Because the Viator API grants transactional access to Viator’s product inventory, it is important we certify that your system meets our minimum criteria and represents an optimized implementation.

More specifically, certification is designed to verify that:

  • Your API endpoint usage follows our guidelines
  • Your system can not generate bookings with invalid data
  • Your travelers receive all information necessary to participate in their tour or activity

How do we certify API implementations?

Before a new partner implementation can be given the go-ahead to launch, Viator must verify that each certification requirement is met.

There are two parts of certification:

  • front end checks
  • back-end checks

In order to complete back-end checks we will ask you to provide answers to questions about endpoint usage in the file available here: Merchant API certification: back-end checks.

Front-end checks may be conducted by:

  • The partner granting Viator access to their testing environment (recommended).
  • A comprehensive call between the partner’s developers and Viator’s technical onboarding team where each certification element is demonstrated.
  • The partner submitting screenshots in the certification form.

When your implementation is ready for certification, please contact our API onboarding team.

Please allow up to 7 business days to receive your initial round of certification feedback, depending on our current volume of demand.

The table below presents a high-level overview of the elements of your API implementation that must be certified before launch. Each element listed here will be covered in greater detail later in this guide.

Adherence to endpoint usage rules

To ensure that the load on our systems is not excessive, we ask that you develop your API implementation in accordance with our endpoint-specific usage rules. If you have a use case which would violate a rule, you must first seek approval from our API Onboarding Team.

Please refer to our API documentation: Update frequency.

delta

Fixed-cadence delta updates

In order to keep your local databases up-to-date without placing an excessive burden on our servers, we recommend the following fixed cadences at which you should poll the content-ingestion endpoints:

Fixed cadences for polling content ingestion endpoints
Endpoint Recommended update cadence
/products/modified-since Every 15-30 minutes following initial ingestion
/availability/schedules/modified-since Every 15-30 minutes following initial ingestion

Fixed-cadence full updates

To ensure your systems reflect any removals of or changes to existing destinations, locations or booking questions, we recommend retrieving full updates from these endpoints as follows:

Fixed cadences for polling content ingestion endpoints

On-demand updates

When ingesting product content, in the event that you encounter an unknown reference – i.e., a new location reference, booking question, tag or destination id – or, if you need to perform a currency conversion for which the last exchange rate you retrieved has expired, we recommend you call the relevant endpoint to resolve the new reference immediately or just after completing the product content update.

Fixed cadences for polling content ingestion endpoints
Endpoint When to update
/exchange-rates Whenever you encounter a currency that you need to convert, but the last-retrieved exchange-rate for that currency-pair is no longer valid due to having expired (according to the expiry value in the response from this endpoint)
/locations/bulk Whenever you encounter a location reference code that you do not yet have the details for (we recommend retrieving location details in batches using this endpoint; therefore, the retrieval of new location data can commence after all new product content is retrieved)
/products/tags Whenever you encounter a tag reference code that you do not yet have the details for
/v1/taxonomy/destinations Whenever you encounter a destination id that you do not yet have the details for
/products/booking-questions Whenever you encounter a booking question identifier that you do not yet have the details for
/v1/taxonomy/attractions Whenever you encounter an attraction identifier that you do not yet have the details for
/reviews/product If the number of reviews available for a product, which is reported in the reviews.totalReviews element in the product content response, has changed compared with its previous value, the reviews for that product should also be refreshed by calling the /reviews/product endpoint. We request that you rate-limit your use of this service to 30 requests per minute.

Content Ingestion

  1. The /products/modified-since endpoint must be used to ingest product content and to keep it up to date. Other product content endpoints (/products/bulk, /products/{product-code}) should not be used for this purpose unless you are ingesting products that haven’t been ingested before (for example, due to additional filtering rules on your end).
  2. The product content updates must be pulled with the /products/modified-since endpoint at least hourly. The recommendation is to refresh it as frequently as possible, for example every 15-30 minutes, to avoid stale data.
  3. To keep content up to date you must use delta updates and not re-ingest full content when an update is not required.
  4. If you are ingesting and storing our content in your local database, please ensure that tags, reviews, destinations, locations, photos and booking questions are cached and refreshed periodically based on the recommended frequency of updates.

Availability Ingestion

  1. The /availability/schedules/modified-since endpoint must be used to ingest availability and pricing schedules and to keep this information up to date. Other availability endpoints (/availability/schedules/bulk, /availability/schedules/{product-code}) must not be used for this purpose unless you are ingesting products that haven’t been ingested before (for example, due to additional filtering rules on your end).
  2. Availability and pricing schedules must be refreshed with the /availability/schedules/modified-since endpoint at least hourly. The recommendation is to refresh it as frequently as possible, for example every 15-30 minutes, to avoid stale data.
  3. Please do not attempt to ingest the full catalog of availability schedules with each ingestion run. Ingest the full catalog once, and then only perform delta updates.
  4. Do not use the availability real-time request with the /availability/check endpoint for ingestion purposes.
  5. If you are converting prices from our supplier currency into your desired currency using the /exchange-rates endpoint, you must ingest exchange rates and refresh them based on the expiry timestamp from the response to this endpoint.

Real-time availability and pricing checks

  1. You must check real-time availability and pricing with the /availability/check endpoint, even if you have already ingested availability schedules.
  2. You must verify the real-time availability of a bookable item*/passenger mix/travel date combination before submitting a booking request for that same bookable item*/passenger mix/travel date combination.
    *bookable item is what a customer actually purchases (i.e. the product or the product option or the start time – whichever is the lowest level on the product)
  3. Real-time availability may not be requested unless the user has selected a travel date and passenger mix (age bands).
  4. The request to /availability/check endpoint must be made with the currency in which you wish to be invoiced by Viator. This way you will be able to verify the final rate in that specific currency and avoid possible pricing discrepancies that could occur due to exchange rate fluctuations. This is especially important if you decided not to use the /exchange-rates endpoint.
  5. In case the /availability/check endpoint returns a different price than previously displayed to the customer based on /schedules, the new price from the availability/check endpoint must be applied in order to proceed with the booking.

Booking hold operations

Full + booking access affiliate API partners are required to implement the /bookings/cart/hold endpoint in order to avoid last-minute availability and pricing changes that might occur during the booking process and in order to make a booking with the /bookings/cart/book endpoint. Partners who use this solution are not allowed to access the /bookings/hold or /bookings/book endpoint.

Using the /bookings/hold or the /bookings/cart/hold endpoint is not mandatory in case of merchant API partners but it is highly recommended to implement the booking hold with either of these endpoints to ensure that the booking will be confirmed at the expected price.

When the /bookings/cart/hold endpoint is used to make a hold, the /bookings/cart/book endpoint must be used to book (not the /bookings/book endpoint).
When the /bookings/hold endpoint is used to make a hold, the /bookings/book endpoint must be used to book (not the /bookings/cart/book endpoint).

 

  1. A booking hold must only be placed when there is strong intention from your customer to purchase (when the customer is moving to the checkout page or when the card payment details are requested).
  2. A booking hold must never be used to check a product’s availability, i.e. you should not call it immediately prior to making the booking to check the availability or price – for this purpose you would need to use the /availability/check endpoint. Booking hold is used to place a pricing and availability (when possible) hold for a short period of time needed to complete the booking (for example when a customer is providing all information required to book at checkout).
  3. It is important to verify the timestamps returned for the availability hold and for the pricing hold to know how long each hold is valid for. If necessary (when the hold expires but the booking hasn’t been confirmed yet), another hold can be done (it will generate a new bookingRef that should be included in the booking request).
  4. A new hold can only be placed in case the previous hold expires.
  5. If you use the API payments solution:
  6. If you use the Viator iframe solution:
    • hostingUrl must match the URL of the page hosting the iframe.
    • VIATOR_FORM must be selected as the value for paymentDataSubmissionMode as well as the hostingUrl must be provided in the /bookings/cart/hold request (URL of the page where the payment form is hosted, it should include the protocol, domain and any non-standard port, excluding the trailing ‘/’).
    • The paymentSessionToken from the /bookings/cart/hold response must be used to initialize the iframe.
    • See our step-by-step guide for the iframe solution here.
  7. The correct currency code must be included in the hold request:
    • Full + booking access affiliate API partners can select one of the following currencies: USD, EUR, GBP, AUD, CAD, CHF, DKK, FJD, HKD, JPY, NOK, NZD, SEK, SGD, THB, ZAR, INR, BRL, TWD, MXN, CLP, IDR, ILS, KRW, PHP, PLN, TRY.
        • 5 currencies are supported for merchant API partners: AUD, CAD, EUR, GBP, USD
  8. The status of the hold must be correctly verified:
    • If you use the /bookings/hold endpoint you should check if the hold is provided for availability and for pricing separately (based on the timestamps) and in case the first hold expires before the booking request is sent place a new hold or verify the price and availability again with the /availability/check endpoint to avoid pricing discrepancies in bookings
    • If you use the /bookings/cart/hold endpoint, you must verify the booking status from the response prior to proceeding with the payment. When the status is REJECTED the customer must be advised that the product is not available to book (based on the rejectionReasonCode returned in the response).

      Making a booking

      1. Booking references (bookingRef and partnerBookingRef) and, when applicable (when using the /bookings/cart/hold endpoint ), cart references (cartRef and partnerCartRef) from the hold must be used when submitting the booking request and all booking details from hold request must match the details from the booking request.
      2. If you use the API payments solution:
        • Use paymentDataSubmissionUrl from the /bookings/cart/hold to submit payment details with /paymentaccounts endpoint instead of constructing the URL manually.
        • x-trip-clientid header must be populated with the partner PID and a partner-generated request identifier must be included in the x-trip-requestid header
        • When creating the payment token with the /paymentaccounts endpoint it is mandatory to provide full credit card details (number, cvv, expMonth, expYear, name) including address (country + postalCode).
        • The country code (alpha-2 code for the customer country) and postal code must be accurate and for the owner of the card details.
        • The sessionAccountToken from the /paymentaccounts response must be used as the paymentToken in the /bookings/cart/book request.
        • The Javascript library must be included on your website and used to send through the device fingerprint.
        • Check that success and failure responses to /bookings/cart/book are handled appropriately and provide a suitable customer experience.
        • See our step-by-step guide for the API payments solution here.
      3. If you use the Viator iframe solution:
        • You need the latest version of Edge, Firefox, Chrome or Safari, other browsers/older versions are not supported.
        • The paymentSessionToken from the /bookings/cart/hold response must be used in the initialization logic called on page load.
        • Implement the onSubmitSuccess and onSubmitError methods to deal with successful or error responses.
        • The country code (alpha-2 code for the customer country) and postal code must be accurate and for the owner of the card details.
        • The paymentToken returned in the object passed to the success callback method from the payment submitForm call must be used as the paymentToken in the /bookings/cart/book request.
        • The Javascript library must be included on your website and used to send through the device fingerprint.
        • Check that success and failure callbacks are handled appropriately and provide a suitable customer experience:
          • Success: Complete the booking
          • Failure: Provide appropriate error message to customer
        • See our step-by-step guide for the iframe solution here.
      4. If you use either the API payments solution or the iframe solution you need to ensure that the fraudPreventionDetails in additionalBookingDetails will be populated appropriately*:
        • Use subChannelId if you have multiple channels (e.g. multi domains) (applicable to partners with multiple channels).
        • Use agencyId if you have for example, agencies in different geographies (applicable to agencies).
        • Use agentId if you have agents that make bookings (applicable to agencies).
        • voucherDeliveryType must be set appropriately based on how vouchers are delivered. Also it wil match one of the 3 predefined values (applicable to all partners)
        • voucherDisplayedPostPayment must be boolean and set appropriately (applicable to all partners).
        • customerMemberSince must be populated if such information is available (applicable to partners with customers that have membership/ are registered).
          *partners must implement where applicable.
      5. In case a timeout has been implemented for the booking service it should not be shorter than 120 seconds (see Timeout).
      Z

      Checking booking status

      1. The status field returned in the response to the booking service must be always verified prior to confirming the booking to the end customer. The booking can be confirmed only when the API returns a CONFIRMED status and it must not be confirmed when the API response returns REJECTED status. In case of an error response or a timeout, the booking status must be verified with the /bookings/status endpoint.
      2. If you support manual confirmation products, you must verify the status of the bookings (confirmed/rejected) by calling the /bookings/status endpoint periodically and communicate the final booking status to travelers in a timely manner. Suppliers have up to 72 hours to confirm/reject the booking request, or it will be automatically rejected 24 hours before the activity start time.
      3. The status of bookings must be checked with the /bookings/status endpoint not more than once every 3 minutes, hourly checks are recommended.
      Q

      Traveler cancellations

      Please note: merchant API partners are required to implement the cancellation API workflow in order to cancel bookings on traveler’s behalf. This flow is optional for full + booking access affiliate API partners but if implemented, the rules described below apply.

      1. The /bookings/{booking-reference}/cancel-quote endpoint must be called in real-time to verify the refund amount prior to canceling the booking.
      2. It is necessary to verify the refundAmount and refundPercentage fields from the /bookings/{booking-reference}/cancel-quote response in order to check the refund amount. All bookings with travel date in the future return “status”: “CANCELLABLE” however this doesn’t mean that the booking is refundable (refundable amounts can vary depending on cancellation policy of the product.).
      3. One of the cancellation reasons from the /bookings/cancel-reasons endpoint must be included in the /bookings/{booking-reference}/cancel request. We recommend monthly refresh of cancellation reasons.
      Q

      Supplier cancellations

      1. In order to automate the process for supplier cancellations it is necessary to call the /bookings/modified-since endpoint at least every 5 minutes and no more frequently than every 10 seconds. In case you are not using API services to get canceled bookings, you will need to rely on emails sent by Viator and ensure that your customer operations team is able to inform travelers about canceled bookings in a timely way (24h customer support required).
      2. In order to stop email notifications for supplier cancellations, it is necessary to acknowledge that the cancellation notification from the /bookings/modified-since/acknowledge endpoint has been received by the time indicated in the bookings[].acknowledgeBy field in the /bookings/modified-since response.
      3. In case you are not using API services to retrieve supplier cancellations, you will need to rely on emails sent by Viator and ensure that your customer operations team is able to inform travelers about canceled bookings in a timely way (24h customer support required).

      Locations

      1. When getting details of location references with /locations/bulk, please make sure to use no more than 500 location references in each request.
      2. Locations should be refreshed on a monthly basis or after each product ingestion cycle if new locations are identified in the product content response.

      Timeout

      1. In case a timeout has been implemented for API services it should not be shorter than 120 seconds. This is especially important in case of the booking endpoints.
      2. When the booking endpoint returns an error or the response times out, the /bookings/status endpoint must be used to verify if the booking has been confirmed prior to communicating the booking status to the customer, or prior to re-booking.

      User flow and platform navigation

      Whether you ingest all product content into your database, or use the API endpoints in real-time, you will need to make sure that your customers are provided with the tools that will allow them to find the right products. The search functionality and filtering options matter significantly when it comes to surfacing relevant products. This has a direct impact on the customer experience and on the revenue generated.

      Please note: Viator doesn’t have strict certification requirements for the build of the search functionality however we are happy to share our best practices and recommendations.

      Search by destination example
      search-by-destination
      Search by attraction example
      search-by-attraction
      Search by product code example
      ”search-by-product-code"/
      Freetext search example
      freetext-search

      Filtering options

      The more filtering options are available, the easier it is to find relevant products. We recommend implementing filtering by:

      • category (tags)
      • attraction (attractionId)
      • price (fromPrice)
      • review rating (reviews.reviewCountTotals.combinedAverageRating)
      • duration (duration)
      • time of day (startTime)
      • cancellation policy (cancellationPolicy.type, FREE_CANCELLATION flag)
      • special offers (SPECIAL_OFFER flag)
      • Likely to sell out / Excellent quality products (tags)

      Please note: most of the revenue generated by Viator products comes from products with Likely to sell out or Excellent quality tags.

      Category filtering example
      category-filtering
      Attraction filtering example
      attraction-filtering
      Other filtering examples
      other-filtering

      Sort orders

      The order in which products are displayed is key to driving conversion as it’s important to first surface high-quality products that result in high conversion. We recommend implementing the following sort orders (available also in the API) with recommended/ featured sort order applied as a default sort order:

      • recommended/ featured (based on quality-related tags, rating, price, revenue generated etc.) – in the API: DEFAULT sort
      • traveler rating – in the API: TRAVELER_RATING sort in /products/search and REVIEW_AVG_RATING in /search/freetext with DESCENDING order
      • price – in the API: PRICE sort with ASCENDING and DESCENDING order
      • duration – in the API: ITINERARY_DURATION sort
      • new products – in the API: NEW_ON_VIATOR sort with ASCENDING order in /products/search and DATE_ADDED sort with DESCENDING order in /search/freetext

      Platform navigation

      It should be easy for users to navigate the site from search results pages to product display pages and back. For this purpose we highly recommend implementing on search results pages and product display pages a clickable breadcrumb that reflects a hierarchy of destinations (country – region – city) or categories (category – subcategory).

      Below are examples from the Viator front-end:

      • search results page
      • product display page
      Search results page example
      search-results-page
      Product display page example
      product-display-page
      A map view with products is another great functionality that could help customers find products based on their location. This feature could be created using the meeting point (logistics.start) or itinerary location (itinerary) available in the product content response.

      Attractions

      Customers often look for products linked to specific attractions, for example a guided tour in the Louvre Museum or a skip the line ticket to the Eiffel Tower.
      With the option to search for products by attraction it would be much easier to find such products.

      This can be done by:

      1. search for products linked to an attraction based on the attractionId – when searching for products with the /products/search or /search/freetext endpoint include attractionId in the request to get all products linked to an attraction.
      2. search for attractions or products linked to specific attraction by attraction name – allow customers to search for products or attractions by name with a freetext input field and pass the attraction name as searchTerm when calling the /search/freetext endpoint.
        • if you specify the searchType as ATTRACTIONS the response will return all attractions that include the searched term within the name or description
        • if you specify the searchType as PRODUCTS the response will return all products that include the searched term within the name or description
      3. creating attraction pages with the list of products available for each attraction. You can get all attractions for each destination using the /v1/taxonomy/attractions endpoint.
      4. presenting the list of attractions linked to a specific destination to enable filtering for products by attraction (based on the /v1/taxonomy/attractions endpoint)
      Example 1 (products linked to an attraction based on attractionId)

      attractions - products by attractionid

      Example 2 (attractions or products linked by attraction name)
      attractions - attraction name
      Example 3 (creating attraction pages)
      attractions - attraction page
      Example 4 (presenting list of attractions)
      attractions - list of attractions

      Please note: attraction data is Viator unique content and it should not be indexed.

      Check out our Front-End Guide for API Partners for useful tips on website optimization and search functionality.

      Product details

      Important product information displayed

      Our Partner API returns all product information which is important for travelers to know before they book. Presence or lack of this information could determine whether the customer proceeds with the booking, as well as it could impact their booking experience, or even result in cancellations or complaints.

      Getting product information

      1. Use the product content endpoints to retrieve important product content to display on your website. Required information includes:
        • product title,
        • images,
        • description,
        • inclusions,
        • exclusions,
        • additional information from the supplier,
        • cancellation policy,
        • language guides,
        • itinerary details,
        • ticket type,
        • meeting point.
      Z

      Requirements

      1. The basic product information from the following fields must be included on product display pages or anywhere else in the booking flow: title, images, description, inclusions,exclusions, additionalInfo, cancellationPolicy,languageGuides, itinerary, ticketInfo, logistics.start,logistics.end.
      2. All product content must be frequently refreshed with the product content endpoints (at least hourly).
      3. In case you are displaying Viator or Tripadvisor reviews (or review rating) from the API, please make sure that the following rules are followed:
        • all reviews are displayed for a product with the correct review rating instead of selective display of reviews with the highest rating;
        • the provider of the reviews (Viator/Tripadvisor) is indicated (for example by including the phrase “collected by Viator and Tripadvisor”)
        • all Viator/Tripadvisor reviews are rendered non-indexable by search engines.

      Best practices

      1. Implementing traveler photos in addition to supplier photos. Minimum 5 images displayed per product.
      2. Displaying the cancellation policy text as returned exactly in the API response instead of modified text (when the Viator cancellation policies are implemented).
      3. Structured itinerary view with stops and descriptions for each stop (similar to itineraries on Viator.com).
      4. Adding reviews along with the review score and promoting products with the highest review score.

      Age bands/ passenger mix

      Tour operators place constraints on the number and age ranges of passengers allowed to book each product. Before you make a booking, you must ensure that the booker’s party consists of an acceptable combination of passengers and their respective age(s) for the product in question. This combination is referred to as the passenger mix.

      Appropriate bucketing

      1. Locate the ageBands array, found in the pricingInfo object in the response from any of the product content endpoints. Please note: Products with per-unit pricing will always have only a TRAVELER age band whereas products with per-person pricing support the following age bands: INFANT, CHILD, YOUTH, ADULT, SENIOR.
      2. The ageBands array will contain information on the particular age bands this product offers. For each individual ageBand, use the startAge and endAge fields to communicate the appropriate age range to the user. For example, an ADULT ageBand with a startAge of 18 and an endAge of 60 may be displayed as “Adult (18-60).” The age range specified for a given ageBand is set by the tour operator and will vary between products, so this process must be performed on a per-product basis.
      Z

      Requirements

      1. Partners must select the appropriate ageBand for each booker, based on the age of the traveler. It’s important to show the startAge and endAge for all age bands. For example, do not display the age for an adult as 18+. The maximum must be displayed even when it’s 120.
      2. When custom age bands are implemented on a partner’s site, it is necessary to validate the traveler’s age for each traveler at checkout and compare it with the age from each Viator age band. The age bands sent in the booking request must correspond with Viator age bands returned in the API.

      Best practices

      1. Avoid excluding products with specific age bands, as this will significantly limit your available inventory.

      2. Collect age bands on the product detail page, just before displaying tour options.

      Correct age band display example

      Correct age band display

      Incorrect age band display example
      Incorrect age band display

      Appropriate limiting

      1. For each individual ageBand, refer to the minTravelersPerBooking and maxTravelersPerBooking fields the pricingInfo object to set upper and lower bounds on the number of travelers of that ageBand. The minTravelersPerBooking and maxTravelersPerBooking fields in the pricingInfo object (requirements on the age band level) must not be confused with the fields of the same name in the bookingRequirements object (requirements on the booking level).Note: The ageBand for products with per-unit pricing will always be TRAVELER.
      2. Locate the bookingRequirements object in the response from any of the product content endpoints. Using the minTravelersPerBooking and maxTravelersPerBooking fields as reference, set upper and lower bounds on the total number of travelers your users may input when booking this product. The number of travelers from the bookingRequirements object refers to the total number of travelers across all ageBands.
      3. If requiresAdultForBooking is true, at least one traveler with the ADULT or SENIOR ageBand must be included in the passenger mix.
      Step 1 example

      “pricingInfo”: {

      “type”: “PER_PERSON”,
      “ageBands”: [

      {
      “ageBand”: “ADULT”,
      “startAge”: 18,
      “endAge”: 60,
      “minTravelersPerBooking”: 0,
      “maxTravelersPerBooking”: 9
      },

      ]

      },

      Step 2+3 example

      “bookingRequirements”: {

      “minTravelersPerBooking”: “1”,
      “maxTravelersPerBooking”: “15”,
      “maxTravelersPerBooking”: false

      },

      Z

      Requirements

      1. Users must not be able to input a total number of travelers greater than the maxTravelersPerBooking value, or less than the minTravelersPerBooking value specified for each individual ageBand (pricingInfo) and for the booking (bookingRequirements).
      2. If requiresAdultForBooking is true, users must not have the option to create a booking without an adult or senior traveler present (but it should be possible to book for a senior without an adult).

      Best practices

      1. Communicate the maximum and minimum travelers to the user. For example: “This experience is bookable by 2-15 travelers.”
      2. If an adult or senior traveler is required, communicate this to the user. For example: “At least one adult or senior per party.”
      3. Do not include the option to select invalid passenger mixes. From a UX perspective, preventing your users from selecting invalid mixes at all is preferable to displaying error messages.

      Availability and pricing

      Calendar view

      The API returns availability and pricing schedules that can be used to create a calendar view with available dates on your website. This data is returned in the /availability/schedules endpoints.

      Instructions:

      1. Check bookable seasons for a product. In case the product returns multiple bookable items (productOptionCode, startTime), verify the bookable seasons for each bookable item (productOptionCode, startTime) based on the startDate and endDate. Only dates from bookable seasons should be displayed as available to book in the calendar view.
      2. For each season check bookable daysOfWeek under pricingRecords. Only bookable days of the week should be displayed as available to book in the calendar view.
      3. Identify the dates returned in the unavailableDates array in the /availability/schedules response for each bookable item (productOptionCode, startTime):
        • When a product has one bookable item – display all unavailableDates as unavailable in the calendar view.
        • When a product has more than 1 bookable item (productOptionCode, startTime): compare the dates returned for each bookable item (productOptionCode, startTime) under unavailableDates in order to identify overlapping days that can be marked in the calendar as unavailable (greyed out). This way you can show available days that have availability for at least one bookable item (productOptionCode, startTime).
      4. In order to display the price in the calendar view, follow the steps below:
        • Check the pricingDetails returned for all seasons to see if the product has discounted price returned under pricingDetails.price.special When special pricing is returned, the discounted price should be used to display the lowest price for a product for the dates it applies to based on travelStartDate and travelEndDate (when returned). It can be used only during the time when the offer is valid as indicated in the offerStartDate and offerEndDate.
        • When a product doesn’t have the special pricing use the details returned for the original pricing from the pricingDetails array.
        • Pull the lowest price returned for ADULT age band. In case a product doesn’t return ADULT age band, use pricing from other age bands, in the following order: SENIOR, YOUTH, CHILD, INFANT.
          Please note:
          • Products with UNIT pricing return only TRAVELER age band and in this case the price is not provided per person but it’s one price per group.
          • Viator is calculating the fromPrice per-person as an average price based on a group of at least two participants (or, the smallest bookable group if more than two participants are required).
        • Select the pricing field which is used on your side to calculate pricing. Please note: if you are a merchant API parthner – you will be invoiced the partnerTotalPrice but you can also use the recommendedRetailPrice (price on Viator.com). As the Viator price may include Viator discounts resulting in the recommendedRetailPrice lower than the partnerTotalPrice, make sure to always compare both fields.
        • Verify the starting price for all product options and start times within the same day and select the lowest price available for each available day (skip unavailableDates).
        • Ensure that pricing is displayed in the correct currency. The /availability/schedules endpoints return the price in supplier’s currency. In case this currency is not supported on your side or you need to display rates in a different currency, make sure that the rates are converted. We recommend using the /exchange-rates endpoint to get the Viator exchange rates.

      Best practices

      1. Show only available dates in the calendar view. Unavailable dates should be greyed out to avoid confusion.
      2. In case you include the price in the calendar view, make sure that it’s correctly calculated and that the currency is also displayed.

      Pricing calculation

      The API returns different fields with pricing information and the structure of the pricing data differs depending on the program setup*. Pricing is added on product option level, often with multiple seasons and start times with pricing information that varies. It’s important to distinguish between per-person and unit pricing in order to calculate the pricing correctly. Additionally, per-person pricing must be verified on age band level and always based on the number of participants from each age band.

      *This is how you need to check the total amount that you will earn from bookings:

      • If you are a merchant API partner, you will be invoiced the amount returned under the partnerTotalPrice and you can add your markup to that price, customers will pay the partnerTotalPrice + your markup.
      • If you are a full + booking access affiliate, you will receive the commission payment equal to the commission amount and customers will pay the recommendedRetailPrice.

      Instructions:

      1. Identify the pricingPackageType returned for a product in the /availability/schedules response. Calculate the pricing based on the value returned for this field. When UNIT pricing is available, one price applies per entire group/unit which size is specified by minTravelers and maxTravelers. In case of PER_PERSON pricing, the price must be multiplied by the number of participants from each age band.
      2. Verify the pricing based on the passenger count from each age band. In case of products with tiered pricing, make sure to use the price applicable to the selected number of travelers from each age band.
      3. Check the total price that you will earn if you are an affiliate API partner (commission) or pay to Viator if you are a merchant API partner (partnerTotalPrice) and add your markup if applicable (merchant API only). See discounted offers under special pricing returned in the /availability/schedules response. Verify the offerStartDate and offerEndDate for the dates when offer is valid and the travelStartDate and travelEndDate fields to know what travel dates the offer applies to. When the travelStartDate and travelEndDate fields are missing the offer extends for the duration of the season.
      Step 2 example

      “pricingDetails”: [

      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 1,
      “maxTravelers”: 10,
      “ageBand”: “INFANT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 0.00,
      “partnerNetPrice”: 0.00,
      “bookingFee”: 0.00,
      “partnerTotalPrice”: 0.00

      }

      }

      },
      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 1,
      “maxTravelers”: 1,
      “ageBand”: “CHILD”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 69.00,
      “partnerNetPrice”: 54.44,
      “bookingFee”: 3.54,
      “partnerTotalPrice”: 57.98

      }

      }

      },
      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 3,
      “maxTravelers”: 10,
      “ageBand”: “CHILD”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 49.00,
      “partnerNetPrice”: 38.66,
      “bookingFee”: 2.51,
      “partnerTotalPrice”: 41.17

      }

      }

      },
      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 2,
      “maxTravelers”: 2,
      “ageBand”: “CHILD”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 59.00,
      “partnerNetPrice”: 46.55,
      “bookingFee”: 3.03,
      “partnerTotalPrice”: 49.58

      }

      }

      },
      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 4,
      “maxTravelers”: 6,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 79.00,
      “partnerNetPrice“: 62.33,
      “bookingFee”: 4.05,
      “partnerTotalPrice”: 66.38

      }

      }

      },
      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 1,
      “maxTravelers”: 1,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 289.00,
      “partnerNetPrice”: 228.06,
      “bookingFee”: 14.82,
      “partnerTotalPrice”: 242.88

      }

      }

      }

      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 3,
      “maxTravelers”: 3,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 99.00,
      “partnerNetPrice”: 78.12,
      “bookingFee”: 5.08,
      “partnerTotalPrice”: 83.20

      }

      }

      }

      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 7,
      “maxTravelers”: 10,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 59.00,
      “partnerNetPrice”: 46.55,
      “bookingFee”: 3.03,
      “partnerTotalPrice”: 49.58

      }

      }

      }

      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 2,
      “maxTravelers”: 2,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 149.00,
      “partnerNetPrice”: 117.58,
      “bookingFee”: 7.64,
      “partnerTotalPrice”: 125.22

      }

      }

      }

      ]

      Step 3 example

      “pricingDetails”: [

      {

      “pricingPackageType”: “PER_PERSON”,
      “minTravelers”: 1,
      “ageBand”: “ADULT”,
      “price”: {

      “original”: {

      “recommendedRetailPrice”: 55.00,
      “partnerNetPrice”: 45.69,
      “bookingFee”: 2.97,
      “partnerTotalPrice”: 48.66

      }

      “special”: {

      “recommendedRetailPrice”: 38.50,
      “partnerNetPrice”: 31.98,
      “bookingFee”: 2.08,
      “partnerTotalPrice”: 34.06,
      “offerStartDate”: “2023-01-02”
      ,
      “offerEndDate”: “2023-02-28”,
      “travelStartDate”: “2023-01-13”,
      “travelEndDate”: “2023-05-31”

      }

      }

      },

      ]

      Z

      Requirements

      1. Pricing must be verified correctly based on the number of travelers from each age band.
      2. Per-person pricing returned in the API response must be multiplied per person and unit pricing must be applied as one price per group/unit.
      3. Viator products should be sold on partner sites at pricing equal or higher than the partnerTotalPrice unless a lower price is intentionally applied to some products (special offers).
      4. Compare the minTravelersPerBooking and maxTravelersPerBooking from product content response with pricing records from /availability/schedules. Make sure that it’s possible to proceed with the booking only for the passenger count with the price returned in the /availability/schedules response. In case the supplier hasn’t provided pricing for a particular age band or the minTravelers / maxTravelers count specified for the pricing details is lower than the count specified in the product content response, it wouldn’t be possible to book for a passenger mix without pricing.
      5. Pricing from the /availability/schedules endpoints (returned in the supplier’s currency), must be converted into the currency that will be displayed to the user/ currency in which the booking will be made.

      Best practices

      1. If possible, display price breakdown per age band in addition to the total price.
      2. If you are a markup merchant and have decided to sell products at the same price as on Viator.com (recommendedRetailPrice), make sure to always verify the partnerTotalPrice that you will be invoiced. Pricing on Viator.com might include Viator discounts and this would result in recommendedRetailPrice lower than the partnerTotalPrice (low-margin products).
      3. Using the Viator exchange rates from the /exchange-rates endpoint for currency conversion.

      The difference between per-person and unit pricing is explained in our API documentation: Per-person and unit pricing.

      Real-time availability and pricing checks

      Before you make a booking, you must use the /availability/check endpoint to verify that the bookable item* is still available for your user’s desired date and passenger mix. This is because the availability or price you ingest for a product might have changed immediately following ingestion; for example, if the product’s supplier happens to change its price or availability in the interim.

      *bookable item is what a customer actually purchases (i.e. the product or the product option or the start time – whichever is the lowest level on the product)

      Requesting real time availability:

      1. Determine the bookable item(s) for the product, and collect one of the following combinations (a bookable item):
        • The product code (productCode)
        • The product code, and the user’s desired start time (productCode and startTime)
        • The product code, and the user’s desired product option (productCode and productOptionCode)
        • The product code, the user’s desired valid start time, and the user’s desired product option (productCode, startTime, and productOptionCode)
        • Note: Each of the above combinations serves a specific purpose in the UI. For example, if you would like to list the availability of all product options and start times for a product, you would only include the productCode. If you just need to check whether the start time requested by the customer is available, you should include productCode and startTime. To keep processing fast, it is important to choose the right request for each scenario.
      2. Collect the desired date and the passenger mix for your user. Valid dates for a product can be retrieved from any of the /availability/schedules/ endpoints, and valid passenger mixes for a product can be retrieved from any of the product content endpoints.
      3. Make a request to the /availability/check endpoint. In the request body, include the following parameters: the product code (productCode), a valid date of travel (travelDate), a valid passenger mix (paxMix), and your desired currency (currency). If necessary for the product in question, include a valid start time (startTime), and/or a valid product option (productOptionCode).
        Step 3 example
        {

        “productCode”: “5713P109”,
        “startTime”: “10:00”,
        “travelDate”“2020-12-12”,
        “currency”: “AUD”,
        “paxMix”: [

        {
        “ageBand”: “ADULT”,
        “numberOfTravelers”: 2
        },
        {
        “ageBand”: “INFANT”,
        “numberOfTravelers”: 1
        }

        ]

        }

        Z

        Requirements

        1. To ensure a product is available, real-time availability of a bookable item must be verified prior to booking. This step must be carried out; it is not sufficient to rely on previously-ingested availability schedule data at this point in the booking workflow.

        2. Calls to the real-time availability endpoint for a product must only be made after the customer has selected a date and a passenger mix. Real-time availability requests must not be used to populate schedules or calendar views.

        Best practices

        • In general, the real-time availability check should be made immediately prior to presenting the user with the option to purchase the item. This will be either immediately before a booking hold is placed, or if no hold will be placed, before a booking request is made. This minimizes the possibility that the customer will attempt to proceed to the payment page only to find that the bookable item is no longer available.
        • If your booking flow doesn’t incorporate booking hold, you can make two /availability/check requests:
          • the first one at the time a date and passenger mix are selected – in order to display available product options and start times,
          • the second one immediately prior to making the booking – in order to double-check availability and pricing.

        This article explains how to work with the pricing in the API: Calculating Product Pricing with the Merchant API.

        Product options

        Products may consist of multiple variants of the tour or experience, such as varying start times and/or product-options. You must ensure that when making a booking, your users may only specify a start time and/or product option that is valid for the product in question. This grouping is referred to as a bookable item. Bookable items are used to check availability and to create bookings.

        Product option title and description displayed

        1. Determine whether the productOptions array is present in the response from any of the product content endpoints. If no productOptions array is present, the product does not include product options.
        2. Referencing the fields in the productOptions array, display the title and description of each productOptionCode.
          }

          Start times included

            1. Locate the bookableItems.seasons.pricingRecords.timedEntries array in the response from any of the availability schedules endpoints. This array will include all data on start times and availability for a product; or, if available, its product options (based on the productOptionCode).
            2. Display each available startTime from the timedEntries array. If a startTime is specific to a productOptionCode, be sure to display this time as a choice within the product option.
            Step 2 example

            “bookableItems”: [

            {

            “productOptionCode”: “TG2”,
            “seasons”: [

            {

            “startDate”: “2021-05-30”,
            “endDate”: “2021-12-31”,
            “pricingRecords”: [

            {

            “daysOfWeek”: [

            “MONDAY”,
            “TUESDAY”,

            ],
            “timedEntries”: [

            {

            “startTime”: “17:00”

            },
            {

            “startTime”: “09:00”

            },

            ],

            }

            ]

            }

            ]

            }

            Z

            Requirements

            1. Bookable items (productOptionCode / startTime) must be displayed accurately, and must be selected by the user prior to any /availability/check or hold requests.

            Best practices

            1. For maximum transparency it’s best to include all start times available for a product option in the same view rather than display the same product option multiple times, separately for each start time.
            Example from Viator.com

            start times viator

            Language guide verified

            1. Display languageGuides (type and language) specific to the relevant productOptionCode object and ask the customer to choose one. If the user chooses to book this product, a valid productOptionCode must be specified in the booking request. Typically, this information is displayed to users once they select a passenger mix and a date.
            Step 1 example

            “productOptions”: [

            {

            “productOptionCode”: “TG1”,
            “description”: “Pickup included”,
            “title”: “8-Hour Valley of the Temples and Scala dei Turchi tour from Palermo”,
            “languageGuides”: [

            {

            “type”: “GUIDE”,
            “language”: “en”,
            “legacyGuide”: “en/SERVICE_GUIDE”

            }
            {

            “type”: “GUIDE”,
            “language”: “it”,
            “legacyGuide”: “it/SERVICE_GUIDE”

            }

            ]

            },

            Z

            Requirements

            1. A valid languageGuide must be selected for languageGuide when applicable and the type and language must match the options returned in the productOptionCode object for the selected product option.

            Best practices

            1. Include information about supported language guides on product display pages (all language guides for a product from languageGuide) as well as at checkout (language guides available for a product option from productOptions.languageGuides or for product from languageGuide when product options are not supported).

            Pickup verified on product option level

            1. Identify if the selected productOptionCode offers pickup based on productOptions.description. When the phrase “pickup included” is returned in productOptions.description, product option offers pickup and the customer has to select from available pickup options at checkout.
            2. When the productOptions.description doesn’t include the phrase “pickup included” the user must not be asked to select the pickup location. Instead, the PICKUP_POINT question must be answered with MEET_AT_DEPARTURE_POINT as the LOCATION_REFERENCE
            Z

            Requirements

            1. When a product returns “pickupOptionType”: “PICKUP_AND_MEET_AT_START_POINT” the pickup service must be verified on the productOption level. Booking requests sent for product options without pickup must include MEET_AT_DEPARTURE_POINT as the LOCATION_REFERENCE provided in the answer to the PICKUP_POINT question. Booking requests made for product options wih pickup must include the pickup location (LOCATION_REFERENCE or FREETEXT) selected by the customer.

            Best practices

            1. For products/ product options without pickup it’s best to display the meeting point location to customers at checkout.
            2. In case of products that offer pickup for selected product options and have a meeting point for other options, there is no need to ask the customer to select the meeting point for the option without pickup (since only the meeting point applies). Instead, the meeting point should be pre-selected and the location reference MEET_AT_DEPARTURE_POINT must be provided in the answer to the PICKUP_POINT question in the booking request per default.

            Booking process

            Booker identified

            When making a booking, you must specify the name of the person making the booking. The booker’s name will appear on the voucher, and is generally the same person who makes the payment. In previous versions of our API, this person was referred to as the ‘lead traveler’.

            Instructions:

            1. On the checkout screen, collect the first and last name of the booker. When making a booking request, this information must be used to populate the firstName and lastName fields in the bookerInfo object. Make sure that the booker fields to collect the booker’s name are differentiated from those for other travelers.
            Step 1 example

            “bookerInfo”: {

            “firstName”: “Jon”,
            “lastName”: “Edel”

            },

            Z

            Requirements

            1. For each booking, collect the first and last name of the booker, and include this information in the booking request.

            Best practices

            1. If possible on your platform, collect the booker’s information at checkout, rather than earlier on in the user journey.
            t

            Booking questions per-person

            Some Viator products require the traveler(s) to provide information to the tour operator at the time of booking. For example, a museum tour may need the names and ages of each traveler, or a helicopter excursion may need the weights of each traveler. The information is collected via booking questions that the user must answer during checkout.

            Make sure to check this article: Implementing Booking Questions with Viator’s Merchant API.

            1. Make a request to the /products/booking-questions endpoint. The response will contain a flat list of all booking questions, including each question’s machine-readable identifier, plain text for display to the user, and parameters for acceptable answers. This data is mostly static and may be stored locally, but we recommend it be refreshed weekly; or ad hoc if an unknown booking question is encountered.
            2. In the product data returned by any of the product content endpoints, locate the bookingQuestions array. This array will contain the id of each booking question for this product.
            3. Validate each id returned in the bookingQuestions array of the product content response against the corresponding id in the response from the /products/booking-questions endpoint.
            4. Identify booking questions that require answers based on the values returned in the required field.
            5. Verify whether the booking question returned for the product must be asked PER_TRAVELER or PER_BOOKING based on the value returned for group field in the /products/booking-questions endpoint.
            6. For booking questions with units identify the values available for units and make sure that only supported units can be selected and sent in the booking request.
            7. On the checkout page, display the label text for each question listed in the product data, and collect the appropriate response from the user before booking.

            Note: For complete details on each individual booking question, please consult our technical documentation.

            Example
            “bookingQuestions”: [

            {

            “legacyBookingQuestionId”: 1,
            “id”: “DATE_OF_BIRTH”,
            “type”: “DATE”,
            “group”: “PER_TRAVELER”,
            “label”: “Date of birth”,
            “required”: “MANDATORY”,
            “maxLength”: 100

            },

            ]

            Z

            Requirements

            1. Products with booking questions require valid answers to those questions to be submitted in the booking request. The answers must be collected from the customer at the time of checkout.
            2. Booking questions must be displayed and managed in accordance with our documentation. Per-person questions must be asked separately for each traveler. Questions classified as ‘mandatory’ must be answered, and each answer must adhere to the requirements detailed in the respective booking question object.

            Best practices

            1. Do not exclude products with booking questions, as this practice will significantly limit your available inventory and sales opportunities.
            2. Any data validation should be implemented on your end, there is no validation in the API for the format of the answer. For example, users should not be allowed to use emojis in their answers.

            Traveler pickup

            Some tours and activities offer traveler pickup, which is managed via booking questions. Pickup locations may be selected from a list of preset locations, or may be input by the traveler.
            Make sure to check this article: Implementing Booking Questions with Viator’s Merchant API.

            Instructions:

            1. Verify whether the pickup service is provided for all product options by checking the value returned for logistics.travelerPickup.pickupOptionType – in this case “pickupOptionType” is “PICKUP_EVERYONE”. When, “pickupOptionType” is “PICKUP_AND_MEET_AT_START_POINT”, you must verify which of the available product options include pickup by checking for the presence of the phrase “Pickup included” anywhere in the productOptions[].description field).
            2. Identify the available arrival/departure modes and their associated conditional booking questions – by checking the values returned in the bookingQuestions array and grouping the booking questions based on the logic from the conditional booking questions table in the Conditional booking questions section of our API documentation.
            3. Get the locations available for pickup from each arrival mode based on the values returned under logistics.travelerPickup.locations[].pickupType. Please note: the following options for pickupType are currently available:
              • AIRPORT – the location is an airport
              • HOTEL– the location is a hotel
              • PORT – the location is a sea port
              • LOCATION – the location is a specific address added by the supplier
              • OTHER – the location is not, in fact a location; rather, it is a pseudo-location, one of: MEET_AT_DEPARTURE_POINT, or CONTACT_SUPPLIER_LATER
            4. Check if the pickup location can be sent as FREETEXT or only as LOCATION_REFERENCE by verifying the value returned for logistics.travelerPickup.allowCustomTravelerPickup(true = FREETEXT allowed).
            5. Display the available options to the user and collect from them their answers. The recommended way to simplify answering this question for the customer is to minimize the simultaneous display of choices via a two-step method on the front-end. See example here.
            6. Send answers provided by the customer in the booking request (see the section Sending answers to booking questions in the booking request).

            Conditional booking questions

            Conditional booking questions logic

            Please note: In order for the logic to be implemented correctly it’s essential to apply the rules described under Conditions and Extra notes below the table:

            Selection of the arrival/departure mode

            Collecting answers to relevant conditional booking questions for that arrival/departure mode:

            booking questions - Collecting answers to relevant conditional booking questions
            Please note: as indicated in the screenshot above, the drop down list of pickup locations (to answer the PICKUP_POINT question based on conditions 1-3 from the table) should include only locations within the pickupType array that comport with the selected TRANSFER_ARRIVAL_MODE (if that question happens to be requested for the product):

            • TRANSFER_ARRIVAL_MODE – AIR“pickupType”: “AIRPORT”
            • TRANSFER_ARRIVAL_MODE – SEA“pickupType”: “PORT”
            • TRANSFER_ARRIVAL_MODE – OTHER“pickupType”: “HOTEL” or “pickupType”: “LOCATION”

            The TRANSFER_ARRIVAL_DROP_OFF and TRANSFER_DEPARTURE_PICKUP questions are related to locations (arrival drop off and departure pickup) however there are no specific location references or conditions that apply to these questions (see the table). Therefore customers should be provided with a freetext input field for these questions and the answers must include the FREETEXT designation if the customer answered freely, via text.

            Z

            Requirements

            1. Where applicable, options for traveler pickup options must be offered as part of the appropriate booking question at checkout.
            2. It is necessary to check if the pickup/drop off service is offered on the product option level. When logistics.travelerPickup.locations is PICKUP_AND_MEET_AT_START_POINT, the product has some product options with pickup and some without. Product options with the pickup service always include the phrase “pickup included” in the productOptions[].description field. If the customer selects a product option without pickup, they should not be asked to select a pickup location at checkout and the “PICKUP_POINT” question should be answered with “MEET_AT_DEPARTURE_POINT”
            3. When a product has the PICKUP_POINT question it is necessary to identify the locations available for pickup in the logistics.travelerPickup.locations array and create the list of locations available for each pickupType. When “allowCustomTravelerPickup”: true, in addition to locations available for pickup, travelers should be provided with a freetext input field to share a custom pickup location (however freetext should not be a default option).

            Best practices

            1. Avoid excluding products with pickup, as this will significantly limit your available inventory (over 50% of Viator products offer pickup)
            2. Ask pickup-related questions at checkout when other booking details are collected.
            3. In case you are thinking about excluding all products with unsupported arrival/departure modes (airport/port/train station/hotel/other) from your inventory, it’s worth implementing only one arrival/departure mode that can be supported for these products. Most products offer hotel pickup therefore in such situations it might be best to limit the options for pickup to only hotel pickup instead of fully excluding the product.
            Q

            Cancellation policy clearly communicated

            To certify your API implementation, we must confirm that your users will be made aware of a product’s cancellation policy before booking. Most Viator products offer a full refund if the booking is canceled up to 24 hours before the start of the tour or activity.

            Please note:

            • If you are a merchant API partner you may adjust the cancellation policy you apply on Viator products. We recommend applying a cancellation policy that is equal to, or more strict than Viator’s policy (as this policy applies to invoicing). For example, you may choose to offer no refund for a product with Viator’s standard cancellation policy, but we don’t recommend offering a refund on a product with an all-sales-final policy as you will be invoiced for bookings made for products with this policy even if they are canceled by you.
            • If you are a full + booking access affiliate partner, you must display the same cancellation policy as returned in the API for all Viator products.

            Displaying the cancellation policy:

            1. Locate the cancellationPolicy object in the response from any of the product content endpoints.
            2. Within the cancellationPolicy object, display the text in the description field to the user. If cancelIfBadWeather and/or cancelIfInsufficientTravelers are true, communicate this to your users (i.e. “this activity may be canceled due to bad weather”).
            Example

            “cancellationPolicy”: {

            “type”: “STANDARD”,
            “description”: “For a full refund, cancel at least 24 hours before the scheduled departure time.”,
            “cancelIfBadWeather”: false,
            “cancelIfInsufficientTravelers”: false,
            “refundEligibility”: […]

            },

            Z

            Requirements

            1. Cancellation policy must be communicated to the booker before purchase.
            2. The standard cancellation policy must be communicated in hours instead of days. For example, it cannot state “A full refund will apply if you cancel at least a day before the activity start time” because this could imply that full refund will be provided if canceled any time a day before whereas the Viator cancellation policy is calculated based on hours left to the experience start time.

            Best practices

            1. Display cancellation policy in writing on the product detail page and on the checkout page.
            2. Support flexible cancellation policy (i.e. the standard Viator policy with free cancellation up to 24 hours before the start time).
            3. Any displayed cancellation policy should be as strict , or more strict than Viator’s policy.
            Viator cancellation policies and processes are explained in this article: All you need to know about cancellations and in the API documentation: Cancellation policy, Cancellation API workflow.

            Contact details included

            Viator collects contact information that will be used for future communication with suppliers about bookings (i.e. regarding entry tickets, pickup location, important information about the tour). The email address and phone number that will be used for this communication must be included in the booking request.

            Please note:

            • Merchant API partners can send either customer’s email, or customer support team’s email, or both separated by a comma (to be cc’d in all communication).
            • Full + booking affiliate API partners must always provide customer’s contact details for supplier communication.

            Please note: all supplier emails will be sent from an encrypted email address and the supplier will not see the customer’s email address.

            Instructions:

            1. Collect the customer’s email address and phone number that will be used for future communication about their booking.
            2. Provide email and phone for direct supplier communication in the communication object in the booking request. This can be either customer’s email, or your customer support team’s email, or both separated by a comma.
            3. Make sure that the phone number provided in the booking request has the correct format and it includes the “+” symbol at the beginning, followed by the country code, followed by the remaining numerals.
            Z

            Requirements

            1. When the customer operation’s team email is provided for supplier communication, it is necessary to have a process in place to handle communication between Viator suppliers and customers. Often this requires a 24 h support.
            2. The “+” symbol and the country code must be included before the phone number.

            Best practices

            1. It’s recommended to share customer’s email for supplier communication to ensure that all messages will be received by customers instantly. Delays in passing information between suppliers and customers may result in issues with bookings.
            2. Merchants choosing to share customer’s email should mention to their customers that they are purchasing a product from a third-party supplier, and that they may, therefore, receive communications regarding the purchase directly from that supplier.

            Read more about supplier communication here.

            Test booking confirmed

            It is important to validate all booking data before creating a booking in order to prevent invalid bookings.

            As part of the booking process, we recommend you use the hold endpoint to place a temporary hold on price and inventory before making any bookings. A booking hold must only be placed when the customer is taking the final steps to make the purchase. This has two benefits:

            • it ensures the customer has time to enter details of the card without the risk of the inventory being sold out or the price changing.
            • it ensures that inventory is only held when there is a strong likelihood of a purchase, therefore not holding inventory that may otherwise be sold to someone else.

            As part of certification, Viator will request that you make a test booking to confirm that your platform is able to appropriately capture and share the booking voucher with the traveler. We also strongly recommend making test bookings to test the validation for each certification element listed above.

            Important: A live booking must never be performed for test purposes, and will be invoiced along with other live bookings. All test bookings should be made in the sandbox environment.

            Instructions:
            Note: These instructions assume that all data provided in the hold and booking requests has been validated in accordance with the requirements outlined in this page.

            1. Select a product to book. You may refer to the above list of products, or any select Viator product with availability.
            2. Make a hold request (recommended). This request body must include the following parameters: product code (productCode), desired currency for pricing information (currency), date of travel (travelDate), and passenger mix (paxMix). Depending on the product or the solution used, you may need to include the product option (productOptionCode) and/or the start time (startTime), paymentDataSubmissionMode and hostingUrl.
            3. Verify the duration of the availability and pricing holds based on validUntil timestamps returned in the hold response and ensure that the hold hasn’t expired before the booking request is made. If necessary, you may call the hold endpoint again to make another hold. In that case ensure that the bookingRef sent in the booking request is the reference returned for the most recent hold.
            4. Make a booking request. The request body for this request must include the same parameters as the request body of the appropriate hold request, otherwise the booking will be rejected. The request body of all booking requests must must include the Viator booking/cart reference returned by the hold request (bookingRef / cartRef), a partner-generated reference number for internal use (partnerBookingRef / partnerCartRef), the first and last name of the person making the booking (bookerInfo object), and the contact info of the person making the booking (communication.phone, optionally communication.email).In addition, the following objects may be required, depending on the product in question or soluton used: languageGuide, bookingQuestionAnswers, paymentToken and optionally additionalBookingDetails.
            5. If the booking in question was for a manual confirmation product, a /bookings/status request must be made to confirm whether the booking has been accepted by the tour operator. This request should be made periodically until the booking is either confirmed or rejected, throughout the 72 hour window following the booking request. When making a /bookings/status request, include either the bookingRef or the partnerBookingRef returned by the relevant booking request.

             

            Step 2 example
            POST https://api.sandbox.viator.com/partner/bookings/hold

            {

            “productCode”: “12546DOUROVALLEY”,
            “productOptionCode”: “TOURANDBOAT”,
            “travelDate”: “2021-10-01”,
            “currency”: “EUR”,
            “startTime”: “08:30”,
            “paxMix”: [

            {
            “ageBand”: “ADULT”,
            “numberOfTravelers”: 1
            }

            ]

            }

            Step 4 example
            POST https://api.sandbox.viator.com/partner/bookings/book

            {

            “productCode”: “12546DOUROVALLEY”,
            “productOptionCode”: “TOURANDBOAT”,
            “currency”: “EUR”,
            “partnerBookingRef”: “BR-555555555”,
            “travelDate”: “2020-11-30”,
            “paxMix”: [

            {
            “ageBand”: “ADULT”,
            “numberOfTravelers”: 1
            }

            ],
            “languageGuide”: {

            “type”: “AUDIO”,
            “language”: “en”,
            “legacyGuide”: “en/SERVICE_AUDIO”
            },

            “bookingRef”: “BR-555555555”,
            “bookerInfo”: {

            “firstName”: “Jon”,
            “lastName”: “Edel”

            },
            “bookingQuestionAnswers”: [

            {
            “question”: “SPECIAL_REQUIREMENTS”,
            “answer”: “NO”,
            “travelerNum”: 1
            }

            ],
            “communication”: {

            “email”: “jedel@tripadvisor.com”,
            “phone”: “5555555555”

            },
            “additionalBookingDetails”: {

            “voucherDetails”: {

            “companyName”: “Viator”,
            “email”: “affiliateapi@viator.com”,
            “phone”: “5555555555”,
            “voucherText”: “Enjoy your adventure!”

            }

            }

            }

            Step 5 example

            {

            “BookingRef”: “BR-555555555”

            }

            Z

            Requirements

            1. A hold request may not be made until the user has indicated a high intent to book. Typically, this means that it may not be called until immediately prior to the user entering their payment details.
            2. A hold request must never be used to check product availability – the /availability/check endpoint must be used for this purpose. 
            3. All data included in a booking request (passenger mix, travel date, booking questions, etc.) must be validated for the product in question before a hold or a booking request is made.
            4. A /bookings/status request must not be made more than once every three minutes, per booking.

            Booking status communicated

            Viator offers three types of products:

            • Instant confirmation type – where the final booking status will be returned instantly in the response to the booking service. The vast majority of Viator products (about 90%) come with instant confirmation. It could also happen that the booking request made for instant confirmation type is rejected in case of last minute availability changes. The probability of this happening is very low, especially when the availability is verified in real-time with /availability/check prior to booking and the availability hold is done. However, you need to be prepared for a scenario when the booking request is rejected and make sure that you can handle it correctly.
            • Manual confirmation type – the booking made for this type of product must be manually confirmed by the supplier. In order to support manual confirmation type products it is necessary to develop additional logic to verify the booking status using API services and inform the traveler.
            • Instant then manual – a mix of both confirmation types depending on how much time is left until the travel date.

            Instructions:

            1. Identify the confirmation type for a product based on the value returned for confirmationType field in the bookingConfirmationSettings object in product content response. The possible values are: INSTANT, MANUAL, INSTANT_THEN_MANUAL.
            2. If you are not able to support products with type MANUAL, INSTANT_THEN_MANUAL, make sure that they are filtered out on your side. In case you ingest products with /products/modified-since endpoint, you will need to exclude products with unsupported confirmation type during the ingestion flow based on the values returned for the confirmationType field. In case you search for products in real-time using the /products/search endpoint, you can request products with a specific confirmation type by including it in the request body.
            3. Verify the booking status returned in the response to the booking endpoint and communicate it to the traveler.
              • Make sure that the booking is confirmed to the customer only when the status is CONFIRMED.
              • When the status is REJECTED, verify the rejectionReasonCode to understand why the booking request has been rejected and communicate to the customer that their booking has not been successful.
              • When the status is PENDING, inform the customer that their booking request is waiting for acceptance from the supplier. The supplier has 72 hours to confirm the booking request or it will be automatically rejected 24 hours before the activity start time. You will need to do periodic booking status checks with the /bookings/status endpoint to verify if the booking has been confirmed or rejected (using the bookingRef or the partnerBookingRef returned in the booking response) and the final status must be communicated to the traveler. Please note that Viator will not send the confirmation email to either you or the traveler.
            Step 2 example

            {

            “filtering”: {

            “destination”: “321”,
            “startDate”: “2023-06-21”,
            “endDate”: “2023-06-22”

            },
            “sorting”: {

            “sort”: “PRICE”,
            “order”: “DESCENDING”

            },
            “confirmationType”: “INSTANT”,
            “pagination”: {

            “start”: 1,
            “count”: 5

            },
            “currency”: “USD”

            }

            Z

            Requirements

            1. The booking status must be correctly verified and the customer must be informed about the status of their booking.
            2. In order to support manual confirmation type products it is mandatory to implement a logic to verify the booking status with the /bookings/status endpoint.
            3. Unsupported product types (confirmation types) must be filtered out on your side.

            Best practices

            1. Implement frequent booking status checks in case of manual confirmation type products (however not more than once every 3 minutes).

            2. We recommend not excluding manual confirmation type products as this would limit the inventory.

            Read more about booking confirmation types here.

            Viator voucher shared with customers

            To ensure that our tour operators honor partner bookings, the traveler must present a voucher which is compatible with the tour operator’s processes. This means that upon booking, the traveler must receive an unmodified booking voucher, as returned by the API. Further, the traveler must be informed what type of voucher the operator accepts: mobile, paper, or both.

            Displaying voucher type:

            1. Locate the ticketInfo object in the response from any of the product content endpoints.
            2. Within the ticketInfo object, display the text in the ticketTypeDescription and ticketsPerBookingDescription fields to the user.

            Accessing the voucher:

            1. Make a valid booking using the booking endpoint. When making your request, you may customize some textual elements of the voucher via the fields in the voucherDetails object of the request body. Note: You may choose to include the logo of your organization on vouchers. This is managed by Viator at the account level and you can add a logo for vouchers in your Partner Dashboard.
            2. Once the booking has been made, provide the traveler access to the voucher returned in the voucherInfo object of the response. You may do this by sharing the link directly with your traveler, or by embedding the voucher into your platform.
            Displaying voucher example

            “ticketInfo”: {

            “ticketTypes”: [

            “MOBILE_ONLY”

            ],
            “ticketTypeDescription”: “Mobile or paper ticket accepted”,
            “ticketsPerBooking”: “ONE_PER_BOOKING”,
            “ticketsPerBookingDescription”: “One per booking”

            },

            Accessing voucher example

            “voucherInfo”: {

            “url”: “https://shop.live.rc.viator.com/ticket?code=1006658118:1176d4aed0e88ffec7faf11e67183edc0cd40d5b5244b30b0cd08aab14f72b4b”,
            “format”: “HTML”

            }

            Z

            Requirements

            1. After a booking is made, the traveler must be able to access an unmodified voucher.

            2. The accepted type(s) of voucher must be communicated to the traveler.

            Best practices

            1. The traveler should be able to access their voucher(s) through the booking confirmation email sent by the partner, or when they log in to their account.
            2. Accepted voucher types should be indicated on the product detail page.
            3. It’s highly recommended for merchant partners to include on vouchers the merchant partner’s name and contact information (email and phone) using the additionalBookingDetails schema. Any customized message for travelers may be displayed on vouchers by sending the text of the message under the additionalBookingDetails.voucherDetails.voucherText field in the booking request:
            Example

            “additionalBookingDetails”: {

            “voucherDetails”: {

            “companyName”: “Viator”,
            “email”: “support@test.com”,
            “phone”: “+61400500600”,
            “voucherText”: “Thank you for booking with us!”

            }

            }

            }

            Please note if you are a merchant API partner: when this information is not included in vouchers, it’s essential to provide customers with the merchant partner’s contact details for booking support in a different way, to ensure that they are able to get in touch with the relevant team when needed (merchant partners are responsible for providing customer support).

            ~

            HTTPS

            Your platform must have a valid HTTPS security certificate during all stages of the checkout process. For the purposes of certification, the checkout process is defined as any part of the booking journey in which customer data is handled.

            Z

            Requirements

            1. All stages of the checkout process must have a valid HTTPS security certificate.

            Best practices

            1. Your entire tours and activities platform is recommended to have a valid HTTPS security certificate.

            Cancellation process

            Most Viator products offer a full or partial refund when canceled by the booker. Cancellations should be managed by the merchant partner via the /bookings/{booking-reference}/cancel endpoint (and are optional for full + booking access affiliate partners). The cancellation API workflow is described here.

            Refund amount communicated prior to cancellation

            API services must be used in real-time to verify the refund amount and communicate it to the traveler before they decide to cancel the booking.

            Instructions:

            1. Get the Viator booking reference (bookingRef) saved from the response to the booking endpoint for the booking that will be canceled.
            2. Using the Viator booking reference (bookingRef), call the /bookings/{booking-reference}/cancel-quote endpoint to check the refund amount for that booking.
            3. Verify the status and the values returned under refundAmount and refundPercentage and communicate it to the traveler.

            Please note: The response will always return “status”: “CANCELLABLE” if it’s before the activity start time however this doesn’t mean that the booking is refundable. In order to check if the refund will be provided it’s necessary to verify values returned for the refundAmount and refundPercentage.

            Z

            Requirements

            1. The refund amount (refundAmount) must be verified in real-time using the /bookings/{booking-reference}/cancel-quote endpoint and communicated to the customer prior to canceling the booking.
            2. The booking can be canceled only when the customer agrees to proceed with the cancellation after they are informed about refund amount.

            Best practices

            1. The most efficient way to implement traveler-initiated cancellations is to enable self-service cancellations. This way you will avoid delays and potential changes to the refund amount that could take place from the time a cancellation is initiated by the customer to the time when the booking is canceled by a customer service agent.
            Q

            Test booking canceled

            This check must be conducted twice – first in sandbox and once the certification is passed and the live API key is shared, the same check must be conducted in production prior to launch to ensure that all bookings and cancellations will be correctly processed.

            Important: Outside of this certification check, a live booking must never be performed for test purposes. All test bookings should be made in the sandbox environment.

            Instructions:

            1. Select a product with future availability, and with Viator’s standard cancellation policy.
            2. Make a valid booking using the booking endpoint. Be sure to select a date more than 24 hours in the future.Note: When working in the production environment, https://api.viator.com/partner/… should replace https://api.sandbox.viator.com/partner/… for all endpoints.
            3. Using the /bookings/{booking-reference}/cancel endpoint, cancel the booking.
            4. Share the booking reference for the canceled booking with our onboarding team. Viator will confirm that booking data is correctly remitted to the Viator system, and will reply with approval to launch (if booking data is correctly remitted), or next steps (if an error is detected).
            Z

            Requirements

            1. The above booking and cancellation must be performed prior to launch. If a partner enters commercial production without completing this certification check, Viator can not guarantee that refunds will be honored.
            2. All bookings with travel date in the future must be canceled by partners using API services and the cancellation API workflow described here.

            Please note:
            We will ask you to conduct a similar check again in the live environment, using your production API key. This check will be the last step before launching our partnership commercially.

            Important: Outside of this certification check, a live booking must never be performed for test purposes. All test bookings should be made in the sandbox environment.

            Viator cancellation policies and processes are explained in this article: All you need to know about cancellations and in the API documentation: Cancellation policy, Cancellation API workflow.

            Miscellaneous

            Appropriate use of branding

            A traveler will book tours or activities through your organization and must be assisted through your customer service processes. To avoid confusion, Viator branding must not be present on your platform. An exception applies to Viator/Tripadvisor reviews which, based on your contractual agreement with Viator, must include the provider of the reviews.

            Z

            Requirements

            1. No Viator branding or mention of Viator may be included on your platform apart from the booking vouchers returned by the API and reviews.
            2. When the Viator/Tripadvisor reviews (text or rating) are implemented, the provider of the reviews (Viator/Tripadvisor) must be indicated (for example by including the phrase “collected by Viator and Tripadvisor”).
            favicon-viator

            Protected Viator Unique Content non-indexed

            The Viator API grants pre-approved partners access to Viator’s unique content. Any of Viator’s unique content you choose to display must not be allowed to be indexed by search engines.

            Z

            Requirements

            1. Content from the following endpoints must not be allowed to be indexed by search engines:
              • /v1/search/attractions
              • /v1/attraction
              • /v1/attraction/reviews
              • /v1/attraction/photos
              • /v1/attraction/products
              • /reviews/product
            2. Content retrieved from the viatorUniqueContent object via a product content endpoint must not be indexable by search engines.

            Read more about the Viator unique content in our API documentation: Protecting unique content.

            PCI DSS compliance

            Please note: this requirement applies only to affiliate API partners with full + booking access

            It is essential for affiliate API partners with booking access to establish and maintain technical and organizational security measures governing the processing of customer payment data and other customer information. Viator will conduct an annual PCI DSS assessment as the Attestation of Compliance (AOC) is valid for 12 months.

            Z

            Requirements

            1. Prior to API launch share an updated Attestation of Compliance (AOC) – this document is required in order to confirm PCI DSS compliance.
            2. Share an update Attestation of Compliance (AOC) annually (based on the expiry date for previous AOC) with the API Integrations Team by emailing affiliateapi@tripadvisor.com.

            Important: failure to share a valid Attestation of Compliance (AOC) will result in deactivation of the service.

            Testing

            To help you with testing, please use this list of products for reference*:

            • Instant confirmation: 100337P2
            • Manual confirmation: 11612P1
            • Single product option: 100427P4
            • Multiple product options: 5516ST5
            • Product with all Viator age bands: 200006P21
            • Booking for a senior without an adult: 177458P2
            • Booking for an infant/ child without an adult/senior: 200006P21
            • Minimum passenger count higher than 1: 33844P1
            • Maximum passenger count lower than 10: 33844P1
            • Different min/max count per age band: 60853P12
            • Multiple language guides and different language guides for different product options: 16168P10
            • Booking questions per person: 5516ST5
            • Single arrival mode: 100427P4, 6977P52
            • Multiple arrival modes: 100014P10, 9895P69
            • Multiple departure modes: 45557P135
            • No arrival mode, only departure mode: 9574P295
            • Pickup added on product option level: 5516ST5, 9025P51
            • Product with selective conditional booking questions – 9811P2
            • Product with “pickupOptionType”: “MEET_EVERYONE_AT_START_POINT” and booking questions – 335316P4
            • Custom pickup location allowed 101650P10
            • Availability only some days of the week: 22710P11
            • Standard cancellation policy: 3991MB_ATVM
            • Custom cancellation policy: 40944P355
            • All sales final cancellation policy: 100014P7

            * Product setup is subject to change, please notify in case the products listed here no longer meet the criteria and we will share new examples for testing.

            Did you find this article useful?