NAV Navbar
javascript
Postman Collection

Introduction

Welcome to the Hornblower API!! You can use our API to access Hornblower API endpoints.

We have language bindings in JavaScript! You can view code examples in the dark area to the right.

This document describes the standards used to establish connectivity between a Hornblower API and your system.

Authentication

Access control is achieved using HTTP’s ​basic access authentication​. Hornblower expects for the Token to be included in API requests to the Hornblower API in a header that looks like the following:

Authorization: Basic MTM3Nzc1OmpmZGQzamdyc3NmZXdmcmZycmU4Zg==

Format

Request Format

Example

const url = "https://devshop.hornblower.com/api";
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: "Basic yourToken",
  },
  body: payload,
  method: "POST",
});

The hornblower API expects regular HTTPS requests that contain data as JSON formatted payloads as per specifications. It is important that the HTTP ​Content-type​ header is set to all requests.

Please note as well that we always return a ​200​ HTTP status code, as long as the response contains an expected payload (this includes error codes).

Datetime Format

Points in time are expressed as strings using the ISO 8601 datetime format.

Example: 2019-10-30T18:30:00-07:00

We always expect the local time with its timezone as UTC offset.

Example: hbsdogco_53 Ticket in San Francisco for the 30th of October 2019 at 18:30 (local time):

Expected:

2019-10-30T18:30:00-07:00

Not expected:

2019-10-30T21:30:00-04:00 / 2019-10-31T01:30:00-00:00

Endpoint

Tours

Get Tours Example

const apiUrl = "https://devshop.hornblower.com/api/gyg/1/get-tours";
const res = await fetch(apiUrl, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  },
  method: "GET",
});

The above command returns JSON structured like this:

{
  "data": {
    "tours": [
      {
        "name": "Alcatraz Day Tour",
        "productId": "hacco_1000016"
      },
      {
        "name": "Chicago Seadog Lakefront Speedboat Tour",
        "productId": "hbsdogco_53"
      },
      {
        "name": "San Francisco Dinner Cruise",
        "productId": "hbsdogco_53"
      }
    ]
  }
}

You can call this endpoint in order to get all available tours. Please contact us to set up tours for you if you don't see any tours returned.

Request sample

GET https://devshop.hornblower.com/api/gyg/1/get-tours

Availability

Get Availabilities Example

const productId = "hbsdogco_53";
const fromDateTime = "2019-11-01T00:00:00-07:00";
const toDateTime = "2019-11-06T23:59:59-07:00";
const withProduct = true;
const apiUrl = "https://devshop.hornblower.com/api/gyg/1/get-availabilities";
const url = `${apiUrl}/?productId=${productId}&fromDateTime=${fromDateTime}&toDateTime=${toDateTime}&withProduct=${withProduct}`;
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  },
  method: "GET",
});

The above command returns JSON structured like this:

{
  "data": {
    "availabilities": [
      {
        "dateTime": "2020-08-05T12:00:00-05:00",
        "productId": "hbsdogco_53",
        "vacancies": 30,
        "cutoffSeconds": 18000,
        "ticketsData": [
          {
            "taxes": [
              {
                "TaxPercentage": 9,
                "TaxIncluded": false,
                "TaxName": "City Amusement Tax"
              },
              {
                "TaxPercentage": 0,
                "TaxIncluded": false,
                "TaxName": "Sales Tax"
              }
            ],
            "ticketPrice": 20.27,
            "ticketDescription": "Adult",
            "isTaxInclusive": false,
            "category": "ADULT",
            "ageRange": {
              "min": 13,
              "max": 54
            }
          }
        ],
        "options": [
           {
             "type": "fromStopId",
             "id": 682,
             "name": "Swanage Pier"
           },
           {
             "type": "toStopId",
             "id": 683,
             "name": "Poole Quay"
           }
        ]
      }
    ]
  }
}

You can call this endpoint in order to get up to date availability information for each product. The response contains the availability for each time slot of the requested product that falls within the supplied time period.

If there is no availability for a timeslot that falls within the range return an entry with 0 availability e.g. vacancies: 0. We do not return a ​NO_AVAILABILITY​ error code when no availability for a timeslot. If there is no availability at all in the given time range we return an empty array.

The parameter withProduct is optional. If that is true, we return ticket detail in time slot level, including ticketPrice, taxes, ticketDescription, ticketType and so on.

For some products, we require customers select departure and destination before reserving tickets. In this case, the availability response contains options which include departure and destination info. The options must be provided when reserve and book tickets if the product is a departure/destination product.

Request sample

GET https://devshop.hornblower.com/api/gyg/1/get-availabilities/?productId=hbsdogco_53&fromDateTime=2019-11-01T00:00:00+02:00&toDateTime=2019-11-05T23:59:59+02:00&withProduct=false

Parameter Type Description
productId String The ID of the requested product in the hornblower’s system.
fromDateTime String Marks the start (inclusive) of the requested time period (ISO 8601).
toDateTime String Marks the end (inclusive) of the requested time period (ISO 8601).
withProduct(Optional) Boolean The flag to return product detail.

Response sample

Fields Type Description
cutoffSeconds Integer An integer representing the cut-off time in seconds.
vacancies Integer An interger representing available time slots
ticketsData(Optional) Array An array contains ticket detail info.
ticketsData[].taxes Array An array of tax object.
taxes[].TaxPercentage Integer An percentage for the tax.
taxes[].TaxIncluded Boolean The Boolean indicates if this tax is included or not.
taxes[].TaxName String The name of the tax
ticketsData[].ticketPrice Float The price base on our agreement.
ticketsData[].ticketDescription String The description of ticket.
ticketsData[].isTaxInclusive Boolean The Boolean indicates if the tax is inclusive or not.
ticketsData[].category String The category we expect in booking call(ADULT, CHILD,INFANT, SENIOR).
options(Optional) Array The array which contains optional options, such as fromStopId and toStopId
options[].type String The option type
options[].id String The option id
options[].name String The option name

Reservation

Make Reservation Example

const url = "https://devshop.hornblower.com/api/gyg/1/reserve";
const payload = {
  data: {
    bookingItems: [
      {
        category: "ADULT",
        count: 2,
      },
      {
        category: "CHILD",
        count: 1,
      },
    ],
    dateTime: "2019-11-01T19:00:00-07:00",
    productId: "hbsdogco_53",
    options: [
      {
        type: "fromStopId",
        id: 682,
        name: "Swanage Pier"
      },
      {
        type: "toStopId",
        id: 683,
        name: "Poole Quay"
      }
    ]
  },
};
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  },
  body: JSON.stringify(payload),
  method: "POST",
});

The above command returns JSON structured like this:

{
  "data": {
    "reservationReference": "hbsf_1C9FC6D3-5FFE-445E-837D-01EFCED94BB2"
  }
}

This call represents the first step of the booking process. It reserves the requested quantities in the Hornblower system for 15 minutes in order to ​guarantee a successful booking within the following 15 minutes. ​

Failure to do so will potentially incur in unconfirmed bookings if the product is no longer available.

HTTP Request

POST https://devshop.hornblower.com/api/gyg/1/reserve

Parameter Type Description
productId String The ID of the requested product in the hornblower’s system.
dateTime String The date/time of the activity to be reserved (ISO 8601).
bookingItems Array A list specifying the number of persons per category in this reservation.
bookingItems[].category String The category for which the number of persons is being provided
bookingItems[].count Integer The number of persons being reserved for the specified category.
options(Optional) Array The array which contains optional options, such as fromStopId and toStopId
options[].type String The option type
options[].id String The option id
options[].name String The option name

Reservation Cancellation

Cancel Reservation Example:

const url = "https://devshop.hornblower.com/api/gyg/1/cancel-reservation";
const payload = {
  data: {
    reservationReference: "hbsf_EBB72E93-3C11-4EFF-8B75-20F868ECFCB2",
  },
};
const res = await fetch(url, {
  headers: Object.assign({
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  }),
  body: JSON.stringify(payload),
  method: "POST",
});

The above command returns JSON structured like this:

{
  "data": {}
}

This endpoint is able to cancel the reservation. This happens by default after 15 minutes.

HTTP Request

POST https://devshop.hornblower.com/api/gyg/1/cancel-reservation

Parameter Type Description
reservationReference String The identifier for the reservation in the hornblower’s system as returned by the reserve call.

Booking

Book Example:

const url = "https://devshop.hornblower.com/api/gyg/1/book";
const payload = {
  data: {
    bookingItems: [
      {
        category: "ADULT",
        count: 2,
      },
      {
        category: "CHILD",
        count: 1,
      },
    ],
    comment: "Require wheelchair assistance.",
    dateTime: "2019-11-01T19:00:00-07:00",
    gygBookingReference: "yourBookingRef",
    language: "en",
    productId: "hbsdogco_53",
    reservationReference: "hbsf_1C9FC6D3-5FFE-445E-837D-01EFCED94BB2",
    travelers: [
      {
        email: "john@john-smith.com",
        firstName: "John",
        lastName: "Smith",
        phoneNumber: "+49 030 1231231",
      },
    ],
    options: [
      {
        type: "fromStopId",
        id: 682,
        name: "Swanage Pier"
      },
      {
        type: "toStopId",
        id: 683,
        name: "Poole Quay"
      }
    ]
  },
};
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  },
  body: JSON.stringify(payload),
  method: "POST",
});

The above command returns JSON structured like this:

{
  "data": {
    "bookingReference": "hbsdogco_2087644",
    "tickets": [
      {
        "category": "ADULT",
        "ticketCode": "0005201907231624562430132112",
        "ticketCodeType": "QR_CODE"
      },
      {
        "category": "ADULT",
        "ticketCode": "0005201907231624562430124303",
        "ticketCodeType": "QR_CODE"
      },
      {
        "category": "CHILD",
        "ticketCode": "0005201907231624562430370991",
        "ticketCodeType": "QR_CODE"
      }
    ]
  }
}

This endpoint confirms a reservation that has been placed previously and provides additional information. The gygBookingReference can be the booking reference in your system in case you need to do some mapping in your system.

HTTP Request

POST https://devshop.hornblower.com/api/gyg/1/book

Parameter Type Description
productId String The ID of the requested product in the hornblower’s system.
reservationReference String The reservation reference returned by the reserve endpoint.
gygBookingReference String The reference of this booking in your system.
dateTime String The date/time of the activity to be reserved (ISO 8601).
bookingItems Array A list specifying the number of persons per category in this reservation.
bookingItems[].category String The category for which the number of persons is being provided.
bookingItems[].count Integer The number of persons being booked for the specified category.
language String ISO 639-1 code of the language in which the product shall be booked.
travelers Array A list of objects containing information about the travelers of this booking. The number of elements does not necessarily match the total indicated by the ​bookingItems​ parameter and in fact will most often include only the lead traveler.
travelers[].firstName String First name of the traveler.
travelers[].lastName String Last name of the traveler.
travelers[].email String Email address of the traveler
travelers[].phoneNumber String Phone number of the traveler
comment String Additional information provided by the traveler e.g. special requests
options(Optional) Array The array which contains optional options, such as fromStopId and toStopId
options[].type String The option type
options[].id String The option id
options[].name String The option name

Get Booking

Get Booking Example:

const apiUrl = "https://devshop.hornblower.com/api/gyg/1";

const getOrder = async ({ url }) =>
  fetch(url, {
    headers: {
      accept: "*/*",
      "content-type": "application/json",
      Authorization: basicToken,
    },
    method: "GET",
  });

// Get Booking by bookingReference
const bookingReference = "hbny_1195572";
const bookingRefUrl = `${apiUrl}/get-booking/?bookingReference=${bookingReference}`;
const Booking = await getOrder({ url: bookingRefUrl });

// Get Booking By gygBookingReference
const gygBookingReference = "yourBookingRef";
const gygBookingRefUrl = `${apiUrl}/get-booking/?gygBookingReference=${gygBookingReference}`;
const Booking = await getOrder({ url: gygBookingRefUrl });

The above command returns JSON structured like this:

{
  "data": {
    "bookingReference": "hbny_1195572",
    "tickets": [
      {
        "category": "adult",
        "ticketCode": "0005201910161959436300613951",
        "ticketCodeType": "QR_CODE"
      },
      {
        "category": "adult",
        "ticketCode": "0005201910161959436300863733",
        "ticketCodeType": "QR_CODE"
      },
      {
        "category": "child",
        "ticketCode": "0005201910161959436300270374",
        "ticketCodeType": "QR_CODE"
      }
    ],
    "orderInfo": {
      "gygBookingReference": "GYG01234500",
      "dateTime": "2019-12-29T18:30:00-05:00",
      "productId": "hbny_1000023",
      "comment": "",
      "travelers": [
        {
          "firstName": "John",
          "lastName": "Smith",
          "phoneNumber": "+49 030 1231231",
          "email": "john@john-smith.com"
        }
      ]
    }
  }
}

This endpoint retrieves the booking information and tickets from hornblower system.

You can get the Booking by bookingReference or gygBookingReference.

HTTP Request

Get Booking by bookingReference

GET https://devshop.hornblower.com/api/gyg/1/get-booking/?bookingReference=hbny_1195572

Get Booking by gygBookingReference

GET https://devshop.hornblower.com/api/gyg/1/get-booking/?gygBookingReference=yourBookingRef

Parameter Type Description
bookingReference String The identifier for the booking in the hornblower’s system as returned by the book call.
gygBookingReference String The identifier for the booking reference in your system as you provide to hornblower.

Booking Cancellation

Cancel Booking Example:

const url = "https://devshop.hornblower.com/api/gyg/1/cancel-booking";
const payload = {
  data: {
    bookingReference: "hbsf_1088812",
  },
};

const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "content-type": "application/json",
    Authorization: basicToken,
  },
  body: JSON.stringify(payload),
  method: "POST",
});

The above command returns JSON structured like this:

{
  "data": {}
}

This endpoint cancels the booking in hornblower system.

HTTP Request

POST https://devshop.hornblower.com/api/gyg/1/cancel-booking

Parameter Type Description
bookingReference String The identifier for the booking in the hornblower’s system as returned by the book call.

Costco

Purchase Gift Cards

Example:

const url = "https://devshop.hornblower.com/api/costco";
const payload = {
  orderNumber: "d74cab55-5dcf",
  quantity: 2,
  giftcardNumbers: ["58582784-2a6d", "7e799ad8-8be1"],
};
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "accept-language": "en-US,en;q=0.9",
    "content-type": "application/json",
    Authorization: "Basic yourToken",
  },
  body: JSON.stringify(payload),
  method: "POST",
});

The above command returns JSON structured like this:

{
  "orderNumber": "d74cab55-5dcf",
  "quantity": 2,
  "totalGiftcardAmount": 100,
  "giftcards": [
    {
      "number": "58582784-2a6d",
      "balance": 50
    },
    {
      "number": "7e799ad8-8be1",
      "balance": 50
    }
  ],
  "invoiceId": "1008536",
  "receiptNo": "55039"
}

This endpoint with POST method can purchase gift cards.

HTTP Request

POST https://devshop.hornblower.com/api/costco

BODY ARGUMENTS

Parameter Type Description
orderNumber String
quantity Number
giftcardNumbers Array of String

Refund Gift Cards

Example:

const url = "https://devshop.hornblower.com/api/costco";
const payload = {
  orderNumber: "d74cab55-5dcf",
  giftcardNumbers: ["58582784-2a6d"],
};
const res = await fetch(url, {
  headers: {
    accept: "*/*",
    "accept-language": "en-US,en;q=0.9",
    "content-type": "application/json",
    Authorization: "Basic yourToken",
  },
  referrer: "local tests",
  body: JSON.stringify(payload),
  method: "DELETE",
});

The above command returns JSON structured like this:

{
  "giftcards": [
    {
      "balance": 0,
      "number": "58582784-2a6d",
      "refunded": true
    },
    {
      "balance": 50,
      "number": "7e799ad8-8be1"
    }
  ],
  "invoiceId": "1008537",
  "receiptNo": "55040"
}

This endpoint with DELETE method can refund the gift cards.

HTTP Request

DELETE https://devshop.hornblower.com/api/costco

URL Parameters

Parameter Type Description
orderNumber String
giftcardNumbers Array of String

Frequently Asked Questions

1. Where can I get the token? Is there an endpoint allowing me to get a token?

There is no endpoint to get the token for now. MTAwMDMzODpqZnFscmpncmhndTNyaXdmZThm is the dev token, you can use it when developing.

2. Where can I get the list of productIds?

We don’t have all the products in our dev environment. You can always use hbsdogco_53 for testing. When you are ready to move on to production, we can provide product mapping information for you.

On prod, you can get the list of active productId by calling Tours endpoint.

3. How long does it take for a reservation to expire?

The reservation will expire in 15 minutes.

4. Why do I get the NO_AVAILABILITY error code when reserving the product?

We always return the tour local time with its timezone as UTC offset no matter where you are. Therefore, you need to exactly use what we return to reserve or book.

5. Can I reserve multiple products at the same time?

We only support one product per reservation. You have to reserve each product one by one if you want to reserve multiple products.

6. If there is any way to refine a search not only by passing in a date but also a time range?

We don't support the time range, we will return all available tours with correct times on that DATE. However, you have to use the correct date and time to reserve and book the tours.

7. How can I get the production credential?

We will issue the production credential only if you are able to book and cancel products successfully. Please please provide both canceled and valid bookingReferences to us. If everything looks good in our system, we will provide a production token to you.

Errors

When an error occurs, Hornblower API categorizes errors into one of the types listed in the table below in order to facilitate error handling and debugging. Additionally, an error message may be added to provide more detailed information.

A successful api call should always return a response object with a ​data​ field, which holds all the specified response data. In case of an error, two fields named ​errorCode​ and e​ rrorMessage should be set accordingly in the response instead.

{
  "errorCode": "VALIDATION_FAILURE",
  "errorMessage": "unknown category"
}
Error Code Meaning
AUTHORIZATION_FAILURE The provided credentials are not valid.
NO_AVAILABILITY The ​reservation​ or ​booking​ call cannot be fulfilled because there is insufficient availability. NO_AVAILABILITY must be use while handling a request for /reserve and /book only.
INVALID_PRODUCT The specified product does not exist or is broken for another reason (excluding availability issues).
INVALID_RESERVATION The specified reservation does not exist or is not in a valid state for the requested operation.
INVALID_BOOKING The specified booking does not exist or is not in a valid state.
VALIDATION_FAILURE The request object contains inconsistent or invalid data or is missing data. It must not be used for cases that are already covered by above errors.
INVALID_TICKET_CATEGORY The reservation or booking call specified a ticket category that is not configured for the requested product. This error requires an additional property in the JSON structure such as:
INTERNAL_SYSTEM_FAILURE An error occurred that is unexpected and/or doesn’t fit any of the types above.