Early August we released a a few big new features for Tours & Activities. First of all, activities can now have multiple "rates" which specify how they are priced and booking constraints. Secondly, we now support "pass" products which can be booked without specifying a date or time. Finally, we also support "on request" activities, where the supplier will accept/reject each booking individually. This article will describe how to use the API to utilize these new features.

While the API is backwards compatible, it is important that you start updating your API calls to use the activity rates to take full advantage of this new functionality. If you do not specify which rate to book, the API will use the "default rate" for the activity. As suppliers start adding multiple rates, you will need to add support for booking rates. Same goes for "pass" products and "on request" products.

Activity Rates

An activity rate is a new layer in the configuration and pricing of activities. In many cases, activities will just have a single rate. However, sometimes suppliers will find it useful to provide more than one rate for their product.

When booking a hotel room, we are used to the concept of multiple rates. When booking your double room, do you want a cheaper "non-refundable rate"? Or maybe you would like the "VIP rate" which includes a bottle of champagne and a three course dinner? Now we are bringing this flexibility to the Tours & Activities industry.

With regards to pricing, it means that you will have a price per pricing category per rate. For example, let's say you have two rates: "standard" and "groups", and two pricing categories: "Adults" and "Children". This means you would have to specify a minimum of 4 prices for the activity: Standard - Adults, Standard - Children, Group - Adults, Group - Children.

Here are the main things you can configure for each rate:

  • Cancellation Policy: You now specify the cancellation policy inside the rate. This means you could have different cancellation policies for different rates (e.g. "refundable" and "non-refundable" rates).
  • Minimum and maximum passenger count: This is useful if you want to create a separate rate for groups. You could, for example, specify a "standard rate" which has a maximum of 9 passengers, and then a "group rate" for a minimum of 10 passengers.
  • Priced per person: This is for future reference, and is currently always "true". However, in the near future, we may specify rates that are not priced per person. Example: Superjeep tour where you can book 1-4 passenger, but the price is always $300.
  • Pick up & drop off: In the rate, you specify whether pick up is "unavailable", "preselected", or "optional" (customer can decide when booking). You also specify whether pick up pricing is "included in the price" or "priced separately". Finally, if priced separately, you can specify whether it is priced per person (in which case a separate cost item is created for each pricing category). Same options can be specified for drop off.
  • Extras: Similarly for extras, you need to specify which extras are available in the rate, and whether they are "preselected" or "optional" (customer can decide when booking). And pricing can be "included in the price" or "priced separately", and also can be configured to be priced per person. So this means firstly, that not all rates may offer or include the same extras. Secondly, it also means that extras can now be priced differently per pricing category (for example, you could offer different pricing of "hiking boots" for adults and children).
  • Start times: List of start times that the rate can be booked for. If this is an empty list, then it is assumed that the rate is applicable for all times. This means you could create a different "morning" and "afternoon" rate for your product.
  • Pricing categories: List of pricing categories that can be booked on the rate. If empty, then it is assumed that all categories can be booked on the rate. This means that you could, for example, allow children to be booked for a morning departure, but not for a midnight departure.
  • Better mapping to OTAs: The big OTAs (Viator, GetYourGuide, Expedia, etc.) do generally not support optional extras/pick up at time of booking. A big benefit of the rates is that you can preconfigure rates for those OTAs, for example one rate with pick up included and another without pick up.

Now let's look at how to get information about rates and book them using the API.

Activity rate specification

When you retrieve an Activity using the API (e.g. by calling /activity.json/{id}), you will get the following relevant information in the response:

"defaultRateId": 123, 

This tells you the ID of the default rate for the product.

...
  "rates": [
    {
      "id": 11608,
      "title": "Standard rate",
      "description": null,
      "index": 0,
      "rateCode": null,
      "minPerBooking": 1,
      "maxPerBooking": 0,
      "pricedPerPerson": true
      "cancellationPolicy": {
        ...
      },
      "pickupSelectionType": "PRESELECTED",
      "pickupPricingType": "INCLUDED_IN_PRICE",
      "pickupPricedPerPerson": true,
      "dropoffSelectionType": "UNAVAILABLE",
      "dropoffPricingType": "INCLUDED_IN_PRICE",
      "dropoffPricedPerPerson": false,
      "extraConfigs": [
        {
          "activityExtraId": 123,
          "selectionType": "OPTIONAL",
          "pricingType": "PRICED_SEPARATELY",
          "pricedPerPerson": true
        },
        {
          "activityExtraId": 456,
          "selectionType": "PRESELECTED",
          "pricingType": "PRICED_SEPARATELY",
          "pricedPerPerson": true
        },
        {
          "activityExtraId": 789,
          "selectionType": "OPTIONAL",
          "pricingType": "INCLUDED_IN_PRICE",
          "pricedPerPerson": true
        }
      ],
      "startTimeIds": [],
      "pricingCategoryIds": []
    }
  ]
...

A few things to mention here:

  • The pickupSelectionType  and dropoffSelectionType options are: "OPTIONAL", "PRESELECTED", "UNAVAILABLE" .
  • The pickupPricingType , dropoffPricingType  and extra pricingType  options are: "PRICED_SEPARATELY" , "INCLUDED_IN_PRICE" .
  • The extra selectionType  options are: "OPTIONAL" , "PRESELECTED" . If an extra is not in the extraConfigs  list, then it is unavailable for this rate.
  • If maxPerBooking is set to zero, it means there is no rate limit on the maximum number of passengers (although the availability and capacity may of course impose limits).

Checking availability and price

Since pricing is now based on rate, and product can have more than one rate, you now get pricing per rate when you check availability. So if you call /activity.json/{id}/availabilities  you will now get pricesByRate :

...
    "pricesByRate": [
      {
        "activityRateId": 123,
        "pricePerCategoryUnit": [
          {
            "id": 111, // pricing category ID
            "amount": {
              "amount": 19990,
              "currency": "ISK"
            }
          },
          {
            "id": 222, // pricing category ID
            "amount": {
              "amount": 14990,
              "currency": "ISK"
            }
          }
        ],
        "pickupPrice": {
            "amount": 5000,
            "currency": "ISK"
        },
        "pickupPricePerCategoryUnit": [
          {
            "id": 111, // pricing category ID
            "amount": {
              "amount": 5000,
              "currency": "ISK"
            }
          },
          {
            "id": 222, // pricing category ID
            "amount": {
              "amount": 2000,
              "currency": "ISK"
            }
          }
        ],
        "extraPricePerUnit": [
          {
            "id": 888, // extra ID
            "amount": {
              "amount": 2000,
              "currency": "ISK"
            }
          }
        ],
        "extraPricePerCategoryUnit": [
          {
            "id": 123, // extra ID
            "prices": [
              {
                "id": 768, // pricing category ID
                "amount": {
                  "amount": 1000,
                  "currency": "ISK"
                }
              }
            ]
          }
        ]
      }
    ]
...

There is one item in the list per rate. The prices within each rate are specified as follows:

  • pricePerCategoryUnit : Use when pricePerPerson  for the rate is true . Has the ID of the pricing category, along with the amount per person.
  • price : Use when pricePerPerson  for the rate is false . Has the price amount per booking for this rate.
  • pickupPrice : Use when pickupPricedPerPerson  is false . Has the price amount for pick up per booking for this rate.
  • pickupPricePerCategoryUnit : Use when pickupPricedPerPerson  is true . Has the ID of the pricing category, along with the amount per person.
  • dropoffPrice  : Use when dropoffPricedPerPerson  is false . Has the price amount for drop off per booking for this rate.
  • dropoffPricePerCategoryUnit  : Use when dropoffPricedPerPerson is true . Has the ID of the pricing category, along with the amount per person.
  • extraPricePerUnit : Use when extraConfigs[].pricePerPerson is false. Has the ID of the extra, and price per unit.
  • extraPricePerCategoryUnit : Use when extraConfigs[].pricePerPerson is true. Has an entry for each extra with the extra ID, and then a prices  list which has an entry and price for each pricing category.

Booking a specific rate

Booking a specific rate is simple, just specify the ID of the rate in rateId :

...
    {
        "activityId": 123,
        "rateId": 234,
        "pricingCategoryBookings": [
            {
                "pricingCategoryId": 11,
                "leadPassenger": true,
                "name": "John Smith",
                "extras": [...]
            },
            {
                "pricingCategoryId": 22,
                "name": "Jane Smith",
                "age": 12,
                "extras": [...]
            }
        ]
    }
...

One important thing to mention here, is that since extras can now be priced per person, you could have different pricing of an extra for e.g. adults versus children. Therefore it is very important that such extras are added to the relevant pricing category booking (instead of adding all to the first passenger).

Pass products

We now support three different bookingType options for activities:

  • DATE_AND_TIME : You must book a specific date and start time. Availability is managed per start time.
  • DATE : You only need to specify the date when booking, no start time. Availability is managed per date.
  • PASS : You do not need to specify date or start time when booking.

It is therefore important that you check the bookingType of the activity you are about to book.

The PASS  type is useful for booking products such as "tourist pass", museum admission, pool passes, etc. - products which do not require you to specify the date or time that you will be arriving.

Pass expiry

Passes can expire. For example, the tourist pass may only be valid for a month after purchase, or maybe you are buying a pass which is only valid until end of 2017.  To see how the pass expired, you need to look at the activity passExpiryType which will be one of the following options:

  • NEVER : The pass actually never expires. Simple as that!
  • FIXED_DATE : The pass is valid until a fixed date as specified in the activity ratefixedPassExpiryDate.
  • RELATIVE_DATE : The pass is valid for X days after purchase, as specified in the activity rate passValidForDays

Pass capacity

In most cases, activities with bookingType  set to PASS , the capacityType  will be set to FREE_SALE.  If it is, however, set to LIMITED , then that means that there is a fixed number of passes available for purchase (remember that this is not per date, so this is the global capacity). An example case for this is a pass for a music festival, where there are a total of 1000 passes available.

In such cases, the remaining number of available passes can be found in the pasesAvailable  field on the activity.

Opening hours

PASS  products will generally specify opening hours. For example, a museum may have opening hours. The opening hours are specified in the activity defaultOpeningHours  field. 

Sometimes opening hours are different over summer vs winter. In such cases the default opening hours may be overridden by specifying seasonalOpeningHours , so make sure you check for that too.

"On request" bookings

The activities now have three different capacityType  options available:

  • FREE_SALE : Can be sold without performing capacity checks.
  • LIMITED : Only a limited number of seats, and remaining capacity must be checked before booking.
  • ON_REQUEST : No capacity is managed. Instead, the operator will manually confirm or reject each booking request.

Therefore, when you book a product which has capacityType  set to ON_REQUEST , you will not get an instant confirmation. Instead the booking will go into a PENDING  state.

Expiry

By default, the pending request will expire after a preconfigured time interval (configured by the supplier). 

Optionally, you can specify a deadline for the supplier. This is a date and time before which the supplier must respond. If the supplier does not respond to the booking request before this deadline, then the booking will be moved to status TIMEOUT. If no deadline is specified, then the default product settings will apply. This is how you would specify the deadline (format is yyyy-MM-dd HH:mm ):

{
    "activityRequest": {
        ...
        "requestDeadline": "2020-06-01 12:00"
        ...
    }
}

HTTP callback on confirm/reject

you can specify a callback URL when booking. When the supplier responds, you will get an HTTP POST to this URL, containing the booking details on JSON format in the request body. This is how you would specify the callback URL in your booking request (other data left out for brevity):

{
    "activityRequest": {
        ...
        "requestCallbackUrl": "https://example.com/bokun-callback"
        ...
    },
}

To determine whether the booking was confirmed or rejected, look at the “status” field in the booking JSON you get in the callback request POST body. It will be one of the three:

  • CONFIRMED : The supplier confirmed the booking.
  • REJECTED : The supplier rejected the booking.
  • TIMEOUT : The supplier did not respond before the deadline.
Did this answer your question?