# Welcome

Welcome to the official OCTO API Developer Hub!

This portal describes the OCTO specification and provides additional resources and recommendations for developers implementing OCTO API to accept and provide data following this standard.&#x20;

You may also directly access [OCTO OpenAPI Specification](https://app.swaggerhub.com/apis-docs/OCTO-API/OCTO-API/1.0) referenced throughout this documentation as well as [OCTO GitHub](https://github.com/octotravel) for additional dev resources.&#x20;

## About OCTO API

[OCTO](https://www.octo.travel/) (Open Connectivity for Tours, Activities, and Attractions) is an open standard API specification for the in-destination experiences sector of the travel industry. The standard defines agreed-upon schemas, endpoints, and capabilities commonly needed when connecting platforms, resellers, OTAs, and other technologies in tours, activities, and attractions. OCTO API is already adopted by a number of industry players. Check out [known implementations](/additional-resources/known-implementations) summarized by our member volunteers.&#x20;

{% hint style="success" %}
**The OCTO API Specification is open source and available to anyone who wants to use it.** You do not need to be a member to use this specification in your business. If, however, you would like to support OCTO and have a voice in how the specification evolves, please consider [becoming a member](https://www.octo.travel/membership).
{% endhint %}

OCTO is developed and administered by a member-based not-for-profit organization OCTO Standards NP Inc. To learn more about OCTO and join the initiative as a member to contribute to the specification development,  please visit <https://www.octo.travel/>.

## What's New

### November 13, 2025

* Published the [Pickups](/capabilities-optional/pickups-new) capability.
* Published the [Dropoffs](/capabilities-optional/dropoffs-new) capability.
* Added the draft [Promotions](/capabilities-optional/promotions-in-dev) capability.
* Replaced the outdated Validator page with current [Self-Certification Tool](/additional-resources/self-certification-tool) information.
* Fixed outdated details and links across [Endpoints and Capabilities](/getting-started/endpoints-and-capabilities).
* Added a new page about the OCTO [Slack Workspace](/additional-resources/slack-workspace).

### May 10, 2025

* Published [Content](/capabilities-optional/content) capability.&#x20;
* Published the proposed draft for the [Pickups](/capabilities-optional/pickups-new) capability. Still pending the Specification Committee review.&#x20;

### April 22, 2024

* Replaced Webhooks capability proposed draft with the [Notifications](/capabilities-optional/notifications) capability placeholder, based on Specification Committee review.
* Added placeholders for [Content](/capabilities-optional/content) and [Pickups](/capabilities-optional/pickups-new) capabilities that are next in the development queue.     &#x20;

### February 3, 2024

* [Pricing](/capabilities-optional/pricing) capability is out of draft

### July 28, 2023

* The official specification [OCTO OpenAPI Specification](https://app.swaggerhub.com/apis-docs/OCTO-API/OCTO-API/1.0) has been updated to correct typos and all the latest changes in line with the 1.0 specification.&#x20;
* <https://docs.octo.travel> has been migrated to GitBook to provide additional tools for users.
* Additional information was added to explain the concept of OCTO API Core Endpoints and additional capabilities to be added to the specification.&#x20;
* Corrected [Booking Cancellation](/octo-api-core/bookings#booking-cancellation) to `POST` `/bookings/{uuid}/cancel` instead of `DELETE` `/bookings/{uuid}` as per OpenAPI 3.0.


# Glossary of Terms

OCTO defines core terms that are re-used throughout the specification. Below we include some of the key terms you need to know before getting started. Refer to [Schemas](/getting-started/schemas) for detailed definitions of fields used in the specification.&#x20;

<table><thead><tr><th width="135">Term</th><th>Definition</th></tr></thead><tbody><tr><td><strong>Reseller</strong></td><td>The reseller, connecting to the Supplier via the API, to further distribute their Products</td></tr><tr><td><strong>Supplier</strong></td><td>The provider of Products, which Reseller is connecting to.</td></tr><tr><td><strong>Product</strong></td><td>The attraction, activity or tour offered by Supplier.</td></tr><tr><td><strong>Option</strong></td><td>A variant of the Product. All products must have at least one option.</td></tr><tr><td><strong>Unit</strong></td><td>The ticket type, e.g. Adult, Child, Senior, etc.</td></tr><tr><td><strong>Unit Item</strong></td><td>A line item per unit within the booking.</td></tr><tr><td><strong>Booking</strong></td><td>A booking made for a specific Product and Option and one or more Unit Items.</td></tr><tr><td><strong>Voucher</strong></td><td>A single admission document (barcode, QRcode, PDF, etc.) that can be used for the entire booking.</td></tr><tr><td><strong>Ticket</strong></td><td>An admission document (barcode, QRcode, PDF, etc.) that can be used per unit item.</td></tr></tbody></table>


# Errors

OCTO API should respond to every request with either a `200 OK` if everything went ok or `400 Bad Request` if it didn't. In the case of the `400 Bad Request`, the response body should similar to this:

```json
{
    "error": "INVALID_PRODUCT_ID",
    "errorMessage": "The Product ID was invalid or missing",
    "productId": "123"
}
```

Error response should always provide `error` and `errorMessage` defined as:

| FIELD          | DESCRIPTION                                                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `error`        | The error code, a table of possible error codes is shown below.                                                             |
| `errorMessage` | A human readable error message which will be translated depending on the language provided by the `Accept-Language` header. |

Depending on the error code we also may pass additional fields which can make it easier to understand what's wrong with your request. In the example above we provide `productId` and pass the value that was sent in the request, indicating that the productId of `123` is not valid.

## List of Error Codes

Below is a list of the error codes and a description of what each means. Further down this page we also provide an example request body for all the error codes that provide additional attributes.

| CODE                      | DESCRIPTION                                                                                                                                                          |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INVALID_PRODUCT_ID`      | Missing or invalid `productId` in the request                                                                                                                        |
| `INVALID_OPTION_ID`       | Missing or invalid `optionId` in the request                                                                                                                         |
| `INVALID_UNIT_ID`         | Missing or invalid `unitId` in the request                                                                                                                           |
| `INVALID_AVAILABILITY_ID` | Missing or invalid `availabilityId` in the request                                                                                                                   |
| `INVALID_BOOKING_UUID`    | Missing or invalid booking uuid, or if you're confirming the booking the booking may have expired already.                                                           |
| `BAD_REQUEST`             | If your request body is not formatted correctly, you have missing required fields or any of the data types are incorrect.                                            |
| `UNPROCESSABLE_ENTITY`    | If your request body is technically correct but cannot be processed for other reasons. e.g. you tried to cancel a booking after the cancellation cutoff had elapsed. |
| `INTERNAL_SERVER_ERROR`   | Hopefully this never happens, but if the backend server is down or there's a network outage.                                                                         |
| `UNAUTHORIZED`            | You didn't send the API Key in the `Authorization` header to an endpoint that requires authentication.                                                               |
| `FORBIDDEN`               | You sent an API Key that was invalid or has been revoked by the backend system. Or you're trying to access an endpoint/resource that you do not have access to.      |

As explained above it's also possible for specific error codes to have additional attributes that help you diagnose what is wrong with your request. Below are all the specific errors that contain these attributes:

### INVALID\_PRODUCT\_ID

{% code overflow="wrap" %}

```json
{  
    "error": "INVALID_PRODUCT_ID",
    "errorMessage": "The Product ID was invalid or missing",
    "productId": "123"
}
```

{% endcode %}

### INVALID\_OPTION\_ID <a href="#invalid_product_id" id="invalid_product_id"></a>

{% code overflow="wrap" %}

```json
{
    "error": "INVALID_OPTION_ID",
    "errorMessage": "The Option ID was invalid or missing",
    "optionId": "321"
}
```

{% endcode %}

### INVLID\_UNIT\_ID

{% code overflow="wrap" %}

```json
{
    "error": "INVALID_UNIT_ID",
    "errorMessage": "The Unit ID was invalid or missing",
    "unitId": "senior-123678"
}
```

{% endcode %}

### INVALID\_AVAILABAILITY\_ID

{% code overflow="wrap" %}

```json
{
    "error": "INVALID_AVAILABILITY_ID",
    "errorMessage": "The Availability ID was invalid or missing",
    "availabilityId": "2020-01-01T10:30+08:00"
}
```

{% endcode %}


# Headers

A set of HTTP headers must be set when making a request to OCTO API.

These headers can include both standard HTTP headers as well as some custom OCTO API headers. Below are the standard request headers and OCTO custom headers that are required to use:

## Request Headers

<table><thead><tr><th width="222">Header</th><th width="148">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>Authorization</code></td><td>Required</td><td>Your <a href="/pages/TrEUmRteRL7b4GOHvRp6">Authentication</a> <code>Bearer</code> token. </td></tr><tr><td><code>Content-Type</code></td><td>Required</td><td>This must be <code>application/json</code> for all <code>POST</code> <code>PATCH</code> and <code>DELETE</code> requests. </td></tr><tr><td><code>Octo-Capabilities</code></td><td>Required</td><td>A list of the Capabilities (their IDs) to be included in the response. See <a href="/pages/ivh3s8xTKnPPXocbtxHe">Endpoints and Capabilities</a> to learn more about Capabilities. </td></tr></tbody></table>

## Response Headers

<table><thead><tr><th width="224">Header</th><th width="153">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>Content-Type</code></td><td>Required</td><td><code>application/json</code></td></tr><tr><td><code>Octo-Capabilities</code></td><td>Required</td><td>A list of the Capabilities (their IDs) initialized with your request. See <a href="/pages/ivh3s8xTKnPPXocbtxHe">Endpoints and Capabilities</a> to learn more about Capabilities. </td></tr></tbody></table>


# Authentication

OCTO uses Bearer authentication. To authenticate requests, an API key must be sent as a Bearer token in the Authorization [header](/getting-started/headers) of your request:

```http
GET /supplier HTTP/1.1
Host: {host}
Authorization: Bearer {your_API_key}
```

{% hint style="info" %}
For security reasons, it's recommended to use a single unique API key per reseller-supplier relationship.&#x20;
{% endhint %}

All API requests must be made over HTTPS. Calls made over plain HTTP should fail. API requests without authentication will also fail. If the token is invalid or is deactivated by Supplier a 403 Forbidden error should be returned.


# Endpoints & Capabilities

OCTO defines several [Core Endpoints](#octo-api-core-endpoints) that apply to most use cases and are **required** for the implementation.&#x20;

Since not all use cases can be fulfilled with just the core endpoints, OCTO provides a concept of [Capabilities](#capabilities), allowing for enhancement to the integrations based on specific needs. Capabilities are **optional** for implementation and to enhance integration by, for example, adding additional information about pricing, content, pickups, etc. &#x20;

## OCTO API Core Endpoints&#x20;

### [Supplier](#suppliers)

<table data-full-width="false"><thead><tr><th width="215">Name</th><th width="102">Method </th><th width="428">Description</th></tr></thead><tbody><tr><td><a href="/pages/kSwPw2aS5dPcZH4tcgYo">Get Supplier</a></td><td><mark style="color:blue;"><code>GET</code></mark></td><td>Returns a single Supplier and associated details for a given Supplier ID.</td></tr></tbody></table>

### [Products](#products)

<table data-header-hidden><thead><tr><th width="217.33333333333331">Name</th><th width="98">Method</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product-list">Get Product List</a></td><td><mark style="color:blue;"><code>GET</code></mark></td><td>Returns a list of Products and associated details. </td></tr><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product">Get Product</a></td><td><mark style="color:blue;"><code>GET</code></mark></td><td>Returns a single Product and associated details for a given Product ID. </td></tr></tbody></table>

### [Availability](#availability)

<table data-header-hidden><thead><tr><th width="221.33333333333331">Name</th><th width="92">Method</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-calendar">Availablity Calendar</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Returns availability for a given Product &#x26; Option as a single object per day. Optimized to be queried for large date ranges and to populate an availability calendar.  </td></tr><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-check">Availability Check</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Returns availability for a given Product &#x26; Option as a single object per start time (or day). You have to perform this step to retrieve an <code>availabilityId</code> required for <a href="/pages/-M958SRccFm9oBgWTAHY">Bookings</a>. </td></tr></tbody></table>

### [Bookings](#bookings)

<table data-header-hidden><thead><tr><th width="220.33333333333331">Name</th><th width="102">Method</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#create-booking">Create Booking</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Creates a booking that reserves the availability (e.g. while you collect payment and contact information from the customer) for a given <code>availabilityId</code>. The booking will remain with the status <code>ON_HOLD</code> until <a href="/pages/-M958SRccFm9oBgWTAHY#booking-confirmation">Booking Confirmation</a> or when the reservation hold expires.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#confirm-booking">Confirm Booking</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Confirms previously placed <a href="/pages/-M958SRccFm9oBgWTAHY#booking-reservation">Booking Reservation</a>, finalizing the booking and making it ready to be used.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#update-booking">Update Booking</a></td><td><mark style="color:orange;"><code>PATCH</code></mark></td><td>Updates/changes your booking before and after it has been confirmed as long as it hasn't yet been redeemed or within the cancellation cutoff window.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#cancel-booking">Cancel Booking</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Cancels your booking. You can only cancel a booking if <code>booking.cancellable</code> is <code>TRUE</code>, and is within the booking cancellation cut-off window.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#extend-pending-booking-expiration">Extend Pending Booking Expiration</a></td><td><mark style="color:green;"><code>POST</code></mark></td><td>Extends the <a href="/pages/-M958SRccFm9oBgWTAHY#booking-reservation">Booking Reservation</a> availability hold if the booking status is <code>ON_HOLD</code>. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking">Get Booking</a></td><td><mark style="color:blue;"><code>GET</code></mark></td><td>Returns the status and details of your existing booking.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking-list">Get Booking List</a></td><td><mark style="color:blue;"><code>GET</code></mark></td><td>Returns the list of the bookings you made for the given filters.</td></tr></tbody></table>

## Capabilities

<table data-full-width="true"><thead><tr><th width="273.3046875" data-type="content-ref">Name</th><th width="166">ID</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/EmfNj3TzxwtSWfk7HeaN">/pages/EmfNj3TzxwtSWfk7HeaN</a></td><td><code>pricing</code></td><td>Adds pricing to most endpoints, giving you advanced static and dynamic pricing capabilities. </td></tr><tr><td><a href="/pages/7IEnbZjoyGyIkX8VJjzp">/pages/7IEnbZjoyGyIkX8VJjzp</a></td><td><code>notifications</code></td><td>Allows to subscribe to be notified when something changes against products, availability or bookings. </td></tr><tr><td><a href="/pages/36PMd9QntPhostm7WXn3">/pages/36PMd9QntPhostm7WXn3</a></td><td><code>content</code></td><td>Extends the core product, option, and unit schemas to provide rich content and images. </td></tr><tr><td><a href="/pages/EmfNj3TzxwtSWfk7HeaN">/pages/EmfNj3TzxwtSWfk7HeaN</a></td><td><code>pickups</code></td><td>Adds structured pickup support, allowing predefined or customer-defined pickup locations, including pickup time windows.</td></tr><tr><td><a href="/pages/vBmF4wUkWjSDO4Mt0uAk">/pages/vBmF4wUkWjSDO4Mt0uAk</a></td><td><code>dropoffs</code></td><td>Adds structured dropoff support, allowing predefined or customer-defined end-of-tour return locations, including time-window details.</td></tr><tr><td><a href="/pages/Dzv3aXouwEGslD5oUKBG">/pages/Dzv3aXouwEGslD5oUKBG</a></td><td><code>promotions</code></td><td><em>🚨 In development, not reviewed or ratified.</em> <br><br>Adds promotional pricing to availability and bookings, exposing promotional offers and their details.</td></tr></tbody></table>

You control which capabilities you want to enable by using the `Octo-Capabilities` [header](/getting-started/headers). For example:

```http
GET /availability HTTP/1.1
Host: {host}
Authorization: Bearer {your_API_key}
Octo-Capabilities: octo/content, octo/offers
```

It's also possible to use the `_capabilities` query parameter if you're unable to use headers:

```http
GET /availability?_capabilities=octo/content,octo/pricing HTTP/1.1
Host: {host}
Authorization: Bearer {your_API_key}
```

You can list all the capabilities with a comma to separate each one.&#x20;


# Schemas

See [OCTO OpenAPI Specification (Swagger)](https://app.swaggerhub.com/apis-docs/OCTO-API/OCTO-API/1.0) for the list of OCTO Schemas.


# Development Support

{% hint style="success" %}
[**Join OCTO as a member**](https://www.octo.travel/join) **to get support with implementing OCTO from fellow members via our 300+ member** [**Slack community**](https://octo-travel.slack.com/)**.**&#x20;
{% endhint %}

If you are a non-member and have questions about implementing OCTO, you may reach out to the volunteer-run inbox <help@octo.travel>. Note, we cannot guarantee a timelily and accurate reply to non-members.&#x20;

Reservation/ticketing system providers can also take advantage of the [OCTO Self-Certification Tool](/additional-resources/self-certification-tool) to test their implementations.


# Supplier

Returns the supplier and associated contact details

## Get Supplier

## Get Supplier

> Returns the supplier and associated contact details.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Supplier"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Supplier":{"type":"object","required":["id","name","endpoint","contact"],"properties":{"id":{"type":"string","description":"Unique identifier for the supplier, used across the platform to represent this supplier entity. This identifier must be unique within the supplier system."},"name":{"type":"string","description":"Name used to identify the supplier within the platform. This name is typically recognized by end customers as the official name of the supplier's business entity. It should clearly represent the supplier's brand or identity to ensure consistency across platforms. For larger multi-venue suppliers, this represents the parent entity's name. Other associated entities or sub-divisions can be specified using the octo/content capability through the venues field."},"endpoint":{"type":"string","format":"uri","description":"The base URL that is prepended to all other API paths. The value should not contain a trailing slash and must follow URI format."},"contact":{"allOf":[{"$ref":"#/components/schemas/SupplierContact"}],"description":"A structured object containing defined contact fields related to the supplier. This includes various communication methods (e.g., website, email, phone) and address information. It ensures standardized contact details that facilitate seamless communication with the supplier for both customers and partners."},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the supplier. This field provides a concise overview of the supplier's business and may be null if no description is available."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of supplier media files hosted at stable URLs. Media enhances the visual and informational representation of the supplier, such as logos and supporting images. This array can be null if no media is available. Note: Media details are intentionally repeated at various levels. "}}},"SupplierContact":{"type":"object","required":["website","email","telephone","address"],"properties":{"website":{"type":"string","nullable":true,"description":"The supplier's official website URL. This should link directly to the supplier's primary website or a dedicated page about the supplier within a supplier system. The URL should not reference a general supplier system but a specific, identifiable supplier presence. This field can be null if no website is available."},"email":{"type":"string","nullable":true,"format":"email","description":"The email address for customer service inquiries, primarily for end customers. This should be a direct and monitored email address for resolving queries and providing support. The field may be null if email support is not offered or the email address is unavailable or not provided in supplier system."},"telephone":{"type":"string","nullable":true,"description":"The customer service telephone number for end customers, formatted according to the E.164 standard. This format includes the country code followed by the national number, with no spaces, dashes, or special characters. This field can be null if telephone support is unavailable or not provided in supplier system."},"address":{"type":"string","nullable":true,"description":"The full mailing address of the location as a single string. It includes street address, city, state, postal code, and country. If no address is provided, this field can be null. For structured details, use the additional address-related fields"}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]}}},"paths":{"/supplier/":{"get":{"operationId":"Suppliers_get","summary":"Get Supplier","description":"Returns the supplier and associated contact details.","parameters":[{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Supplier"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Supplier"]}}}}
```


# Products

Fetch the product for the given id or the list of products available to you.

## Get Product List

## Get Products

> Fetch the list of products.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Products"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]}}},"paths":{"/products/":{"get":{"operationId":"Products_GetProducts","summary":"Get Products","description":"Fetch the list of products.","parameters":[{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Product"}}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Products"]}}}}
```

## Get Product

## Get Product

> Fetch the product for the given id.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Products"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"GetProductRequest.id":{"name":"id","in":"path","required":true,"description":"The product id","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]}}},"paths":{"/products/{id}":{"get":{"operationId":"Products_GetProduct","summary":"Get Product","description":"Fetch the product for the given id.","parameters":[{"$ref":"#/components/parameters/GetProductRequest.id"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Products"]}}}}
```


# Availability

The first step when making a sale is to check for availability. Note if `allowFreesale` is set to true on the [product](/octo-api-core/products) then this step is optional but it is advised you check it anyway if you can to check for closures.

OCTO has two main availability calls:

[Availability Calendar](#availability-calendar) endpoint is designed to be highly optimized and returns a single object per day. It's designed to be queried for large date ranges and the result is used to populate an availability calendar.

[Availability Check](#availability-check) endpoint may be slightly slower as it will return an object for each individual departure time (or day).&#x20;

## Availability Calendar

## Availability Calendar

> This endpoint is highly optimised and will return a single object per day. It's designed to be queried for large date ranges and the result is used to populate an availability calendar.\
> \
> When the end user selects an open date you can call on \`/availability\` endpoint to get the \`availabilityId\` to create the booking

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Availability"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"AvailabilityCalendar":{"type":"object","required":["localDate","available","status","vacancies","capacity","openingHours"],"properties":{"localDate":{"type":"string","description":"The specific date for querying availability on Availability Calendar endpoint. This field must follow the ISO 8601 date format (e.g., 2024-11-18). It ensures standardized representation of dates across different systems."},"available":{"type":"boolean","description":"Indicates whether there is any remaining availability for the specified date.\ntrue: Availability exists.\nfalse: Fully booked or unavailable."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability date:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining quantity (highest remaining vacancies from all availabilities of this day). Should be nulled or omitted when status is FREESALE."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability date. "},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorBadRequest":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"AvailabilityCalendarBody":{"type":"object","required":["productId","optionId"],"properties":{"productId":{"type":"string","description":"The product id."},"optionId":{"type":"string","description":"The option id."},"localDateStart":{"type":"string","description":"Start date to query for (YYYY-MM-DD)."},"localDateEnd":{"type":"string","description":"End date to query for (YYYY-MM-DD)."},"units":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityUnit"},"description":"A list of units."},"currency":{"type":"string","description":"Can be used only when pricing capability is used."}}},"AvailabilityUnit":{"type":"object","required":["id","quantity"],"properties":{"id":{"type":"string","description":"The unit id."},"quantity":{"type":"integer","description":"The quantity of the unit."}},"description":"A list of units."}}},"paths":{"/availability/calendar":{"post":{"operationId":"Availabilities_AvailabilityCalendar","summary":"Availability Calendar","description":"This endpoint is highly optimised and will return a single object per day. It's designed to be queried for large date ranges and the result is used to populate an availability calendar.\n\nWhen the end user selects an open date you can call on `/availability` endpoint to get the `availabilityId` to create the booking","parameters":[{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityCalendar"}}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorBadRequest"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Availability"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityCalendarBody"}}}}}}}}
```

## Availability Check

{% hint style="danger" %}
**A reseller has to perform** [**Availability Check**](#availability-check) **to retrieve an** `availabilityId` **in order to make a** [**Booking Reservation**](/octo-api-core/bookings#booking-reservation)**, so this endpoint is critical for the booking flow.**&#x20;
{% endhint %}

## Availability Check

> This endpoint is slightly slower as it will return an object for each individual departure time (or day). You have to perform this step to retrieve an \`availabilityId\` in order to confirm a sale, so if you just want to use this endpoint and skip the calendar endpoint then that's perfectly ok.\
> \
> You must pass in one of the following combinations of parameters for this endpoint:\
> \- \`localDate\`\
> \- \`localeDateStart\` and \`localDateEnd\`\
> \- \`availabilityIds\`

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Availability"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorBadRequest":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"AvailabilityCheckBody":{"type":"object","required":["productId","optionId"],"properties":{"productId":{"type":"string","description":"The product id."},"optionId":{"type":"string","description":"The option id."},"localDateStart":{"type":"string","description":"Start date to query for (YYYY-MM-DD). Required if `localDateEnd` is set."},"localDateEnd":{"type":"string","description":"End date to query for (YYYY-MM-DD). Required if `localDateStart` is set."},"availabilityIds":{"type":"array","items":{"type":"string"},"description":"Filter the results by the given ids."},"units":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilityUnit"},"description":"A list of units."},"currency":{"type":"string","description":"Can be used only when pricing capability is used."}}},"AvailabilityUnit":{"type":"object","required":["id","quantity"],"properties":{"id":{"type":"string","description":"The unit id."},"quantity":{"type":"integer","description":"The quantity of the unit."}},"description":"A list of units."}}},"paths":{"/availability/":{"post":{"operationId":"Availabilities_AvailabilityCheck","summary":"Availability Check","description":"This endpoint is slightly slower as it will return an object for each individual departure time (or day). You have to perform this step to retrieve an `availabilityId` in order to confirm a sale, so if you just want to use this endpoint and skip the calendar endpoint then that's perfectly ok.\n\nYou must pass in one of the following combinations of parameters for this endpoint:\n- `localDate`\n- `localeDateStart` and `localDateEnd`\n- `availabilityIds`","parameters":[{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Availability"}}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorBadRequest"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Availability"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityCheckBody"}}}}}}}}
```


# Bookings

## Create Booking

{% hint style="danger" %}
**A reseller has to perform** [**Availability Check**](#availability-check) **to retrieve an** `availabilityId` **in order to make a** [**Booking Reservation**](/octo-api-core/bookings#booking-reservation)**.**
{% endhint %}

## Booking Reservation

> Reserving availability when making a booking. The steps to make a reservation are:\
> \
> 1\. \*\*Check Availability\*\*: Check the availability on the \[/availability]\(docs/octo/branches/main/5b08f5f75e75d-availability-check) endpoint to retrieve an \`availabilityId\`\
> 2\. \*\*Booking Reservation\*\* (this step): Create a booking that reserves the availability while you collect payment and contact information from the customer. The booking will remain with status \`ON\_HOLD\` until the booking is confirmed or the reservation hold expires.\
> \
> The availability for the booking is held for the amount of time equal to the\`expirationMinutes\` parameter (if provided), up to an internal limit set by either the supplier or the OCTo provider. The \`utc\_expires\_at\` parameter in the response object will indicate when a reservtion will expire. A reservation can be extended by calling the \[/bookings/{uuid}/extend]\(docs/octo/branches/main/2c7924ab9128f-extend-reservation) endpoint.\
> \
> A reserved booking can be confirmed after the customer finalizes their choice on the \[/bookings/{uuid}/confirm]\(docs/octo/branches/main/614d1613b2d70-booking-confirmation) endpoint provided the reservation had not expired.<br>

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidUnitID":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"Missing or invalid `unitId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidAvailabilityID":{"type":"object","required":["availabilityId"],"properties":{"availabilityId":{"type":"string","description":"Missing or invalid `availabilityId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnprocessableEntity":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BookingReservationBody":{"type":"object","required":["productId","optionId","unitItems"],"properties":{"uuid":{"type":"string","format":"uuid","description":"A unique UUID to identify the booking. Setting this value acts like an idempotency key preventing you from double booking."},"productId":{"type":"string","description":"The product ID for this booking."},"optionId":{"type":"string","description":"The option ID for this booking."},"availabilityId":{"type":"string","description":"The availability ID for the selected timeslot."},"expirationMinutes":{"type":"integer","description":"How many minutes to reserve the availability, otherwise defaults to the supplier default amount."},"notes":{"type":"string","description":"Optional notes for the booking."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/BookingUnitItem"},"description":"An list of unit items that will be included in the booking."},"currency":{"type":"string","description":"Can be used only when pricing capability is used."}}},"BookingUnitItem":{"type":"object","required":["unitId"],"properties":{"uuid":{"type":"string","format":"uuid","description":"The unit item unit ID."},"unitId":{"type":"string","description":"A unique UUID to identify the unit, same as the booking uuid except per unit."}}}}},"paths":{"/bookings/":{"post":{"operationId":"Bookings_BookingReservation","summary":"Booking Reservation","description":"Reserving availability when making a booking. The steps to make a reservation are:\n\n1. **Check Availability**: Check the availability on the [/availability](docs/octo/branches/main/5b08f5f75e75d-availability-check) endpoint to retrieve an `availabilityId`\n2. **Booking Reservation** (this step): Create a booking that reserves the availability while you collect payment and contact information from the customer. The booking will remain with status `ON_HOLD` until the booking is confirmed or the reservation hold expires.\n\nThe availability for the booking is held for the amount of time equal to the`expirationMinutes` parameter (if provided), up to an internal limit set by either the supplier or the OCTo provider. The `utc_expires_at` parameter in the response object will indicate when a reservtion will expire. A reservation can be extended by calling the [/bookings/{uuid}/extend](docs/octo/branches/main/2c7924ab9128f-extend-reservation) endpoint.\n\nA reserved booking can be confirmed after the customer finalizes their choice on the [/bookings/{uuid}/confirm](docs/octo/branches/main/614d1613b2d70-booking-confirmation) endpoint provided the reservation had not expired.\n","parameters":[{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorInvalidUnitID"},{"$ref":"#/components/schemas/ErrorInvalidAvailabilityID"},{"$ref":"#/components/schemas/ErrorUnprocessableEntity"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingReservationBody"}}}}}}}}
```

## Confirm Booking

## Booking Confirmation

> This endpoint confirms the booking so it's ready to be used.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"BookingConfirmationRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidUnitID":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"Missing or invalid `unitId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidAvailabilityID":{"type":"object","required":["availabilityId"],"properties":{"availabilityId":{"type":"string","description":"Missing or invalid `availabilityId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnprocessableEntity":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BookingConfirmationBody":{"type":"object","required":["contact"],"properties":{"emailReceipt":{"type":"boolean","description":"Whether you want OCTO Cloud to email the guest a copy of their receipt and tickets. (defaults to false)"},"resellerReference":{"type":"string","description":"Your reference for this booking. Also known as a Voucher Number."},"contact":{"allOf":[{"$ref":"#/components/schemas/BookingContact"}],"description":"Contact details for the main guest who will attend the tour/attraction. Contact BODY can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/BookingUnitItem"},"description":"An array of unit items that will be included in the booking. This allows you to provide contact details or a reseller reference for each unit item. Be careful to make sure you include ALL unit items that you also had in the original booking reservation request, if you provide more or less than in the booking reservation call this will change the number of unit items being purchased also."}}},"BookingContact":{"type":"object","properties":{"fullName":{"type":"string","description":"The full name of the booking holder or the ticket holder. Can also be retrieved as an alias for the concatenation of `firstName` and `lastName`"},"firstName":{"type":"string","description":"The first name of the booking holder or the ticket holder."},"lastName":{"type":"string","description":"The last name of the booking holder or the ticket holder."},"emailAddress":{"type":"string","format":"email","description":"The email address of the booking holder or the ticket holder."},"phoneNumber":{"type":"string","description":"The phone number of the booking holder or the ticket holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment."},"postalCode":{"type":"string","description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","description":"Optional notes for the booking."}}},"BookingUnitItem":{"type":"object","required":["unitId"],"properties":{"uuid":{"type":"string","format":"uuid","description":"The unit item unit ID."},"unitId":{"type":"string","description":"A unique UUID to identify the unit, same as the booking uuid except per unit."}}}}},"paths":{"/bookings/{uuid}/confirm":{"post":{"operationId":"Bookings_BookingConfirmation","summary":"Booking Confirmation","description":"This endpoint confirms the booking so it's ready to be used.","parameters":[{"$ref":"#/components/parameters/BookingConfirmationRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorInvalidUnitID"},{"$ref":"#/components/schemas/ErrorInvalidAvailabilityID"},{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnprocessableEntity"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingConfirmationBody"}}}}}}}}
```

## Cancel Booking

## Booking Cancellation

> For cancelling bookings. You can only cancel a booking if \`booking.cancellable\` is \`TRUE\`, and is within the booking cancellation cut-off window.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"BookingCancellationRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorUnprocessableEntity":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BookingCancellationBody":{"type":"object","properties":{"reason":{"type":"string","description":"A text value describing why the cancellation happened."},"force":{"type":"boolean","description":"Whether you want OCTO Cloud to email the guest a copy of their receipt and tickets. (defaults to false)"}}}}},"paths":{"/bookings/{uuid}/cancel":{"post":{"operationId":"Bookings_BookingCancellation","summary":"Booking Cancellation","description":"For cancelling bookings. You can only cancel a booking if `booking.cancellable` is `TRUE`, and is within the booking cancellation cut-off window.","parameters":[{"$ref":"#/components/parameters/BookingCancellationRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnprocessableEntity"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCancellationBody"}}}}}}}}
```

## Get Booking List

## Get Booking

> Fetch the status of an existing booking.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"GetBookingRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]}}},"paths":{"/bookings/{uuid}":{"get":{"operationId":"Bookings_GetBooking","summary":"Get Booking","description":"Fetch the status of an existing booking.","parameters":[{"$ref":"#/components/parameters/GetBookingRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"]}}}}
```

## Get Booking

## Get Booking

> Fetch the status of an existing booking.

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"GetBookingRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]}}},"paths":{"/bookings/{uuid}":{"get":{"operationId":"Bookings_GetBooking","summary":"Get Booking","description":"Fetch the status of an existing booking.","parameters":[{"$ref":"#/components/parameters/GetBookingRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"]}}}}
```

## Update Booking

## Booking Update

> Updates a booking before and after it has been confirmed as long as it hasn''t been redeemed or within the cancellation cutoff window. To know if the booking can be updated check the booking''s \`cancellable\` field. If the booking can be cancelled, it can also be updated. It''s generally preferred to update a booking rather than cancelling it and rebooking

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"BookingUpdateRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidUnitID":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"Missing or invalid `unitId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidAvailabilityID":{"type":"object","required":["availabilityId"],"properties":{"availabilityId":{"type":"string","description":"Missing or invalid `availabilityId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnprocessableEntity":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BookingUpdateBody":{"type":"object","properties":{"resellerReference":{"type":"string","description":"Your reference for this booking. Also known as a Voucher Number."},"productId":{"type":"string","description":"The product ID."},"optionId":{"type":"string","description":"The option id."},"availabilityId":{"type":"string","description":"The availability ID for the selected timeslot."},"expirationMinutes":{"type":"integer","description":"How many minutes to reserve the availability, otherwise defaults to the supplier default amount."},"notes":{"type":"string","description":"Optional notes for the booking."},"emailReceipt":{"type":"boolean","description":"Whether you want OCTO Cloud to email the guest a copy of their receipt and tickets. (defaults to false)."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/BookingUnitItem"},"description":"An array of unit items in the booking. To retain or modify existing unit items, you must include the unit item with the associated uuid, otherwise that unit item will be removed."},"contact":{"allOf":[{"$ref":"#/components/schemas/BookingContact"}],"description":"Contact details for the main guest who will attend the tour/attraction. Contact BODY can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)."}}},"BookingUnitItem":{"type":"object","required":["unitId"],"properties":{"uuid":{"type":"string","format":"uuid","description":"The unit item unit ID."},"unitId":{"type":"string","description":"A unique UUID to identify the unit, same as the booking uuid except per unit."}}},"BookingContact":{"type":"object","properties":{"fullName":{"type":"string","description":"The full name of the booking holder or the ticket holder. Can also be retrieved as an alias for the concatenation of `firstName` and `lastName`"},"firstName":{"type":"string","description":"The first name of the booking holder or the ticket holder."},"lastName":{"type":"string","description":"The last name of the booking holder or the ticket holder."},"emailAddress":{"type":"string","format":"email","description":"The email address of the booking holder or the ticket holder."},"phoneNumber":{"type":"string","description":"The phone number of the booking holder or the ticket holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment."},"postalCode":{"type":"string","description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","description":"Optional notes for the booking."}}}}},"paths":{"/bookings/{uuid}":{"patch":{"operationId":"Bookings_BookingUpdate","summary":"Booking Update","description":"Updates a booking before and after it has been confirmed as long as it hasn''t been redeemed or within the cancellation cutoff window. To know if the booking can be updated check the booking''s `cancellable` field. If the booking can be cancelled, it can also be updated. It''s generally preferred to update a booking rather than cancelling it and rebooking","parameters":[{"$ref":"#/components/parameters/BookingUpdateRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorInvalidUnitID"},{"$ref":"#/components/schemas/ErrorInvalidAvailabilityID"},{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnprocessableEntity"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdateBody"}}}}}}}}
```

## Extend Pending Booking Expiration

## Booking Update

> Updates a booking before and after it has been confirmed as long as it hasn''t been redeemed or within the cancellation cutoff window. To know if the booking can be updated check the booking''s \`cancellable\` field. If the booking can be cancelled, it can also be updated. It''s generally preferred to update a booking rather than cancelling it and rebooking

```json
{"openapi":"3.1.0","info":{"title":"OCTO API Specification","version":"0.0.0"},"tags":[{"name":"Bookings"}],"servers":[{"url":"http://localhost:8080/api/octo","description":"","variables":{}},{"url":"https://ventrata-api-1011165921260.us-central1.run.app/api/octo","description":"","variables":{}}],"security":[{"BearerAuth":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"Bearer"}},"parameters":{"BookingUpdateRequest.uuid":{"name":"uuid","in":"path","required":true,"description":"The UUID of the booking","schema":{"type":"string"}},"RequestHeaders.octoCapabilities":{"name":"Octo-Capabilities","in":"header","required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"RequestHeadersContent":{"name":"Accept-Language","in":"header","required":false,"description":"This optional request header allows to specify preferred languages for content in the response. A language code that specifies the language of the product content. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain). This header supports a comma-separated list of language tags with optional quality values (q) to indicate priority, such as en-US, fr-CA;q=0.8, fr;q=0.7, which prioritizes U.S. English, followed by Canadian French, and general French. This header is defined in the HTTP/1.1 specification (RFC 7231) and is commonly used for internationalized websites and services to enhance user experience. For more details, visit MDN Web Docs: Accept-Language - HTTP | MDN. Note this only determines preference and does not guarantee location has content available in the desired language.","schema":{"type":"string"}}},"schemas":{"Booking":{"type":"object","required":["id","uuid","testMode","resellerReference","supplierReference","status","utcCreatedAt","utcUpdatedAt","utcExpiresAt","utcRedeemedAt","utcConfirmedAt","productId","optionId","cancellable","cancellation","freesale","availabilityId","availability","contact","notes","deliveryMethods","voucher","unitItems"],"properties":{"id":{"type":"string","description":"A unique identifier generated by the supplier system for the booking. This ID ensures traceability and must be unique within the system."},"uuid":{"type":"string","format":"uuid","description":"An optional idempotency key set when creating a booking to prevent duplicate bookings in case of retries. Used for API calls."},"testMode":{"type":"boolean","description":"Indicates whether the booking was created in test mode. If true, it is a test booking."},"resellerReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"supplierReference":{"type":"string","nullable":true,"description":"A reference provided by the reseller to identify the booking."},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"Represents the current state of the booking:\nON_HOLD: Awaiting confirmation.\nEXPIRED: Not confirmed within the hold expiration time.\nCONFIRMED: Successfully confirmed.\nCANCELLED: The booking was canceled.\nPENDING: Awaiting external confirmation.\nREDEEMED: The booking has been used."},"utcCreatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was created."},"utcUpdatedAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC when the booking was last updated, if applicable."},"utcExpiresAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date times in UTC for when this booking is due to expire if the status is ON_HOLD."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC at when the booking was redeemed, if applicable."},"utcConfirmedAt":{"type":"string","format":"date-time","nullable":true,"description":"An ISO8601 date time in UTC when the booking was confirmed, if applicable."},"productId":{"type":"string","description":"The ID of product booked."},"product":{"allOf":[{"$ref":"#/components/schemas/Product"}],"description":"The object of booked product. "},"optionId":{"type":"string","description":"The ID of option booked."},"option":{"allOf":[{"$ref":"#/components/schemas/Option"}],"description":"The ID of option booked."},"cancellable":{"type":"boolean","description":"The object of booked option."},"cancellation":{"type":"object","allOf":[{"$ref":"#/components/schemas/BookingCancellation"}],"nullable":true,"description":"A boolean field indicating whether this booking can be cancelled."},"freesale":{"type":"boolean","description":"Indicates if the booking was made without checking availability."},"availabilityId":{"type":"string","nullable":true,"description":"The ID of availability booked."},"availability":{"type":"object","allOf":[{"$ref":"#/components/schemas/Availability"}],"nullable":true,"description":"The availability object that was booked."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Customer contact details for the booking (see unit object for per ticket / unit details)."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this booking are delivered.\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket. These will be provided in the ticket object.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document. These will be provided in the voucher object.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"voucher":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":"Details for voucher-based delivery, provided when VOUCHER is one of deliveryMethods."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/UnitItem"},"description":"An array of unit items included in the booking."},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"BookingStatus":{"type":"string","enum":["ON_HOLD","CONFIRMED","EXPIRED","CANCELLED","REDEEMED","PENDING","REJECTED"]},"Product":{"type":"object","required":["id","internalName","reference","locale","allowFreesale","instantConfirmation","instantDelivery","availabilityRequired","availabilityType","deliveryFormats","deliveryMethods","redemptionMethod","options"],"properties":{"id":{"type":"string","description":"The unique identifier for the product, used across the platform to check availability, create bookings, etc. This identifier must be unique within the scope of the supplier’s system to ensure accurate referencing and operations."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the product. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"locale":{"type":"string","description":"The language code specifying the primary language in which the product operates. It must conform to the IETF BCP 47 standard, which defines language tags for localization (e.g., en-US for American English, fr-FR for French (France), es-ES for Spanish (Spain))."},"timeZone":{"type":"string","description":"The IANA Time Zone identifier indicating the product's location (e.g., America/New_York, Europe/London)."},"allowFreesale":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"instantConfirmation":{"type":"boolean","description":"Indicates whether the customer’s tickets or vouchers are delivered immediately after the booking is confirmed. If false, resellers must manage delayed ticket delivery processes."},"instantDelivery":{"type":"boolean","description":"This indicates whether the Reseller can expect immediate delivery of the customer's tickets. If `false` then the Reseller MUST be able to delay delivery of the tickets to the customer."},"availabilityRequired":{"type":"boolean","description":"Indicates whether an availabilityId is required when creating a booking. If set to false, bookings can be made without specifying a travel date, creating open-dated bookings."},"availabilityType":{"allOf":[{"$ref":"#/components/schemas/AvailabilityType"}],"description":"Specifies the type of availability for the product:\nSTART_TIME: For products with fixed departure times (e.g., walking tour at set times during the day).\nOPENING_HOURS: For products where customers select a date and can visit anytime during operating hours (e.g., museums general admission ticket valid at any time when museum is open)."},"deliveryFormats":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryFormat"},"description":"Lists the formats in which tickets or vouchers for this product are delivered. Each format specifies how the tickets or vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product."},"deliveryMethods":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryMethod"},"description":"Specifies all supported methods of how tickets or vouchers for this product are delivered in the booking response:\nTICKET: Delivered individually per unit in the booking, where each person or unit receives a separate ticket.\nVOUCHER: Delivered as a single voucher for the entire booking, consolidating all units under one document.\nThis field ensures clarity on the format of ticket or voucher delivery to resellers and customers."},"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the product can be redeemed by the customer:\nDIGITAL: The ticket or voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed ticket or voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this product."},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"},"description":"The list array of all options (variations of the product). Each product must have at lest one option. See Option for a detailed on the object."},"defaultCurrency":{"type":"string","description":"Is on the object when Pricing capability is requested. Default currency for this product, if you omit the currency parameter on future endpoints this is the value the reservation system will fallback to."},"availableCurrencies":{"type":"array","items":{"type":"string"},"description":"Is on the object when Pricing capability is requested. All the possible currencies that we accept for this product."},"pricingPer":{"allOf":[{"$ref":"#/components/schemas/PricingPer"}],"description":"Is on the object when Pricing capability is requested. Indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased."},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"AvailabilityType":{"type":"string","enum":["START_TIME","OPENING_HOURS"]},"DeliveryFormat":{"type":"string","enum":["PDF_URL","QRCODE","CODE128","PKPASS_URL"]},"DeliveryMethod":{"type":"string","enum":["VOUCHER","TICKET"]},"RedemptionMethod":{"type":"string","enum":["DIGITAL","PRINT","MANIFEST"]},"Option":{"type":"object","required":["id","default","internalName","reference","availabilityLocalStartTimes","cancellationCutoff","cancellationCutoffAmount","cancellationCutoffUnit","requiredContactFields","restrictions","units"],"properties":{"id":{"type":"string","description":"A unique identifier for the option within the product. This ID is critical for identifying specific options during bookings or other API interactions."},"default":{"type":"boolean","description":"Indicates whether the option is the default selection.\ntrue: This option should be rendered and selected first in customer-facing interfaces.\nfalse: The option is not default and requires manual selection."},"internalName":{"type":"string","description":"The internal name used by the supplier to refer to the option. This name is for internal or operational purposes and may differ from the public, customer-facing name. The customer-facing name is defined separately in the title field under the octo/content capability."},"reference":{"type":"string","nullable":true,"description":"An optional internal code used by the supplier to refer to the product. This field is useful for supplier-specific workflows or cross-referencing. It can be null if no reference code exists for the product."},"availabilityLocalStartTimes":{"type":"array","items":{"type":"string"},"minItems":1,"description":"An array containing all possible start times for the option that can be returned during availability. For example a tour with multiple departure times may have multiple:[\"09:00\", \"14:00\", \"17:00\"]."},"cancellationCutoff":{"type":"string","description":"A text description of the option's cancellation policy, providing clear guidelines to customers."},"cancellationCutoffAmount":{"type":"integer","description":"The numeric value of the cutoff period for cancellations, relative to start time or closing hour (of opening hours product)"},"cancellationCutoffUnit":{"allOf":[{"$ref":"#/components/schemas/CancellationCutoffUnit"}],"description":"The time unit associated with the cutoff period. Possible values are:\nhour: Cutoff is measured in hours.\nminute: Cutoff is measured in minutes.\nday: Cutoff is measured in days."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"An array specifying the contact fields required to confirm a booking. These apply to the lead traveler, not individual tickets. Possible values:\nfirstName: The first name of the traveler.\nlastName: The last name of the traveler.\nfullName: The full name of the traveler.\nemailAddress: The email address of the traveler.\nphoneNumber: The phone number of the traveler.\npostalCode: The postal code of the traveler.\ncountry: The country of the traveler.\nnotes: Optional notes from the traveler.\nlocales: Preferred language/localization preferences."},"restrictions":{"allOf":[{"$ref":"#/components/schemas/OptionRestrictions"}],"description":"Specifies the limitations on booking the option."},"units":{"type":"array","items":{"$ref":"#/components/schemas/Unit"},"description":"The list array of all units (ticket types) available for this product. Each unit represents a specific type of ticket (e.g., Adult, Child). See Unit for a detailed on the object."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","description":"The public, customer-facing name of the product. This name is displayed to end customers and should accurately represent the product for marketing and sales purposes"},"shortDescription":{"type":"string","nullable":true,"description":"A brief, customer-facing description of the product. This field provides a concise overview of the product and can be null if no description is available."},"description":{"type":"string","nullable":true,"description":"A detailed description of the product, offering in-depth information about it and relevant details. This field can be null if extended details are not provided."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the product's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the product’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."},"faqs":{"type":"array","items":{"$ref":"#/components/schemas/FAQ"},"description":"An array containing frequently asked questions (FAQs) related to the product. This field is designed to address common customer inquiries by providing clear and concise answers, enhancing the customer experience and reducing potential confusion. Each object represents a single question and its corresponding answer. Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"},"description":"A list of media files hosted at stable URLs. Media enhances the visual and informational representation of the product, supporting images, videos, or documents.\nNote: Media details are intentionally repeated at both product and option levels. Suppliers should use the level most relevant for the resource. Resellers must merge media information for customer presentation."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"A list of geographical locations associated with the product. These locations can represent an itinerary where the order of locations matters, such as for tours or experiences, or simply a list of related locations linked to the product. This field is particularly useful for map-dependent reseller platforms, as it provides geographic and contextual details to enhance customer understanding and platform integration. Each object in the array represents a single related location and includes the following fields:"},"categoryLabels":{"type":"array","items":{"$ref":"#/components/schemas/CategoryLabel"},"description":"A list of labels representing the categories applicable to the product or experience. These categories help customers quickly understand the nature, format, or features of the product. The predefined category labels are based on Google's Product Categories for Things to Do, ensuring alignment with industry standards. OCTO has also added custom categories to cover additional popular offerings. OCTO welcomes suggestions for additional categories to ensure consistency and better coverage. Please contact the team to propose updates to the specification."},"durationMinutesFrom":{"type":"integer","description":"Indicates the duration of the product or experience in minutes. If the duration is flexible, this represents the typical minimum duration."},"durationMinutesTo":{"type":"integer","nullable":true,"description":"If a number: Represents the maximum in flexible duration of the product or experience in minutes, defining a range.\nIf null: Indicates that the duration is exact and matches the value of durationMinutesFrom."},"commentary":{"type":"array","items":{"$ref":"#/components/schemas/Commentary"},"description":"A list of commentary options available for the product. Each object in the array specifies the format and language of the commentary."}}},"CancellationCutoffUnit":{"type":"string","enum":["hour","minute","day"]},"ContactField":{"type":"string","enum":["firstName","lastName","emailAddress","phoneNumber","country","notes","locales","allowMarketing","postalCode"]},"OptionRestrictions":{"type":"object","required":["minUnits","maxUnits"],"properties":{"minUnits":{"type":"integer","nullable":true,"description":"The minimum number of units (tickets) that can be purchased in a single booking. A null value indicates no minimum."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units (tickets) that can be purchased in a single booking. A null value indicates no maximum."}}},"Unit":{"type":"object","required":["id","internalName","reference","type","restrictions","requiredContactFields"],"properties":{"id":{"type":"string","description":"The unique identifier for this unit within the scope of the option. This ID ensures that each unit can be uniquely referenced and managed."},"internalName":{"type":"string","description":"An internal name for the unit, used for backend purposes and not visible to customers. This field helps with identifying and managing the unit in the supplier’s system."},"reference":{"type":"string","nullable":true,"description":"An optional internal reference code used by the supplier for identification purposes. This field may not be unique and is meant for operational use."},"type":{"allOf":[{"$ref":"#/components/schemas/UnitType"}],"description":"This is the base unit type for this unit definition. A value of TRAVELLER must only be used in replacement of ADULT, CHILD, INFANT, YOUTH, STUDENT, MILITARY or SENIOR. "},"restrictions":{"allOf":[{"$ref":"#/components/schemas/UnitRestrictions"}],"description":"Specifies booking or usage restrictions for the unit."},"requiredContactFields":{"type":"array","items":{"$ref":"#/components/schemas/ContactField"},"description":"Lists the contact information required per ticket for the unit. Possible values include:\nfirstName: First name of the ticket holder.\nlastName: Last name of the ticket holder.\nfullName: Full name, as a combination of first and last name.\nemailAddress: Email address of the ticket holder.\nphoneNumber: Phone number of the ticket holder.\npostalCode: Postal code for identification purposes.\ncountry: Country code (ISO 3166-1 alpha-2).\nnotes: Additional notes or special instructions.\nlocales: Locale preferences (IETF BCP 47 tags)."},"pricingFrom":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public-facing name of the unit, designed to be displayed to customers. This should clearly convey the nature of the unit, such as \"Adult\" or \"Student\"."},"shortDescription":{"type":"string","description":"A concise summary of the unit, offering key details to customers. This helps in differentiating units and highlighting important characteristics."},"features":{"type":"array","items":{"$ref":"#/components/schemas/Feature"},"description":"An array of structured objects describing various aspects of the unit's features, grouped into clear categories. These include details about what is included, excluded, emphasized, essential, or safety-related, ensuring transparency and enhancing the option’s appeal to customers. Note: Features are intentionally repeated at both product and option levels, allowing suppliers to specify details where most applicable. Resellers must combine information from both levels for a comprehensive customer view."}}},"UnitType":{"type":"string","enum":["ADULT","YOUTH","CHILD","INFANT","FAMILY","SENIOR","STUDENT","MILITARY","OTHER"]},"UnitRestrictions":{"type":"object","required":["minAge","maxAge","idRequired","minQuantity","maxQuantity","paxCount","accompaniedBy"],"properties":{"minAge":{"type":"integer","description":"Minimum age to purchase the unit."},"maxAge":{"type":"integer","description":"Maximum age to purchase the unit."},"idRequired":{"type":"boolean","description":"Indicates if identification (e.g., student ID) is required for redemption."},"minQuantity":{"type":"integer","nullable":true,"description":"Minimum number of units that must be purchased (e.g., 2 tickets). Null means no minimum."},"maxQuantity":{"type":"integer","nullable":true,"description":"Maximum number of units allowed in a single booking. Null means unlimited."},"paxCount":{"type":"integer","description":"The number of people each unit represents (e.g., 1 family ticket = 4 pax)."},"accompaniedBy":{"type":"array","items":{"type":"string"},"description":"Specifies if this unit must be accompanied by another unit (e.g., an infant ticket must be purchased with an adult ticket). Array of unit IDs which must be booked together. "},"minHeight":{"type":"integer","description":"Minimum height required for this unit (e.g., for amusement park rides)."},"maxHeight":{"type":"integer","description":"Maximum height allowed."},"heightUnit":{"type":"string","description":"Unit of height measurement (e.g., \"cm\" or \"in\") used for values of minHeight, maxHeight."},"minWeight":{"type":"integer","description":"Minimum weight required."},"maxWeight":{"type":"integer","description":"Maximum weight allowed."},"weightUnit":{"type":"string","description":"Unit of weight measurement (e.g., \"kg\" or \"lb\") used for values of minWeight, maxWeight."}}},"Pricing":{"type":"object","required":["original","retail","net","currency","currencyPrecision","includedTaxes"],"properties":{"original":{"type":"integer","description":"Represents the advertised marketing price, which must be equal to or higher than pricingFrom.retail. Typically used for strike-through pricing, it highlights the original or component-based value of the product when the retail price reflects a discount or bundled offer. For example, a package product combining multiple components (e.g., hotel + tour + meals) may have a total component value of $500 (original), while the bundled retail price is $400. In such cases, the original price is displayed to show savings.This field should only be shown when it is higher than pricingFrom.retail and must accurately reflect a valid reference price, ensuring transparency and trust."},"retail":{"type":"integer","description":"The supplier’s recommended sale price, including all taxes and fees. This is the price charged to end customers and represents the total cost."},"net":{"type":"integer","nullable":true,"description":"The wholesale price charged to the reseller, including all taxes and fees. This price reflects the amount the reseller pays to the supplier."},"currency":{"type":"string","description":"Specifies the currency used for the prices provided in the pricingFrom object. The value must adhere to ISO 4217 currency codes (e.g., USD, EUR, JPY) to ensure consistency across systems."},"currencyPrecision":{"type":"integer","description":"All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: price / (10 ** currencyPrecision) where ** is to the power of e.g. Math.pow(10, currencyPrecision)."},"includedTaxes":{"type":"array","items":{"$ref":"#/components/schemas/Tax"},"description":"This field defines the number of decimal places used for the currency in the pricingFrom object, ensuring precise representation and preventing rounding errors during calculations. For example, in currencies like USD, which have a precision of 2, prices are expressed in cents (e.g., $45.00 is represented as 4500). In currencies like JPY, which have a precision of 0, prices are expressed as whole yen amounts (e.g., ¥4500 is represented as 4500). By aligning with the specific decimal requirements of different currencies, this field guarantees accurate pricing calculations and consistent handling across various currency formats."}}},"Tax":{"type":"object","required":["name","retail","original","net"],"properties":{"name":{"type":"string","description":"The name of the tax or fee, such as \"VAT\", \"City Tax\", or \"Service Charge\". This field provides clear labeling of the tax or fee being applied, making the pricing structure easier to interpret."},"retail":{"type":"integer","description":"The value of the tax or fee included in the retail price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the end-customer price attributable to the specific tax or fee."},"original":{"type":"integer","description":""},"net":{"type":"integer","nullable":true,"description":"The value of the tax or fee included in the net price, expressed in the same currency as the pricingFrom.currency. This value indicates the portion of the reseller’s cost attributable to the specific tax or fee."}}},"Feature":{"type":"object","required":["shortDescription","type"],"properties":{"shortDescription":{"type":"string","nullable":true,"description":"A brief summary of a specific feature, providing quick and precise information about an aspect of the product."},"type":{"allOf":[{"$ref":"#/components/schemas/FeatureType"}],"description":"Specifies the category of the feature to ensure clear and organized communication. Each category serves a distinct purpose:\n\nINCLUSION: Details what is included in the product offering (e.g., \"Hotel pickup included,\" \"Lunch provided,\" \"All equipment supplied\"), emphasizing the product's completeness and value.\nEXCLUSION: Lists what is not included (e.g., \"Gratuities not included,\" \"Admission tickets not provided\"), managing customer expectations and reducing ambiguity.\nHIGHLIGHT: Emphasizes the product's key selling points or unique aspects (e.g., \"Skip-the-line access to the Eiffel Tower,\" \"Expert-guided tour\"), captivating potential customers by showcasing standout qualities.\nPREBOOKING_INFORMATION: Contains essential details customers need to know before booking (e.g., \"Not suitable for children under 3 years,\" \"Wear sturdy footwear\").\nPREARRIVAL_INFORMATION: Offers details to prepare customers for their experience before arrival (e.g., \"Arrive 15 minutes early,\" \"Bring a printed ticket\").\nREDEMPTION_INSTRUCTION: Provides clear instructions on how to redeem the product or service (e.g., \"Show your booking confirmation at the ticket counter,\" \"Scan your QR code upon entry\").\nACCESSIBILITY_INFORMATION: Highlights accessibility-related details (e.g., \"Wheelchair accessible,\" \"No elevators available\").\nADDITIONAL_INFORMATION: Supplies supplementary details that add context or clarity (e.g., \"Pets allowed with prior notice,\" \"Multilingual guides available\").\nBOOKING_TERM: Describes terms related to the booking process (e.g., \"Reservations must be made at least 48 hours in advance,\" \"No changes allowed after booking\").\nCANCELLATION_TERM: Explains the terms and conditions for cancellations (e.g., \"Free cancellation up to 24 hours before the start time,\" \"Non-refundable\").\nThis structured classification enhances the product's appeal, ensures transparency, and facilitates informed decision-making for resellers and customers."}}},"FeatureType":{"type":"string","enum":["INCLUSION","EXCLUSION","HIGHLIGHT","PREBOOKING_INFORMATION","PREARRIVAL_INFORMATION","REDEMPTION_INSTRUCTION","ACCESSIBILITY_INFORMATION","ADDITIONAL_INFORMATION","BOOKING_TERM","CANCELLATION_TERM"]},"FAQ":{"type":"object","required":["question","answer"],"properties":{"question":{"type":"string","description":"The text of the frequently asked question. This should be a well-phrased question that reflects typical customer concerns or queries about the product (e.g., \"Is hotel pickup included?\", \"What is the cancellation policy?\"). Note: FAQs are intentionally repeated at both product and option levels, enabling suppliers to address questions specific to each context. Resellers must combine FAQs from both levels for customer presentation."},"answer":{"type":"string","description":"The detailed response to the corresponding question. Answers should be accurate, informative, and written in a way that resolves customer uncertainty (e.g., \"Yes, hotel pickup is included within a 10-mile radius of the city center.\", \"Cancellations are free up to 24 hours before the activity.\")."}}},"Media":{"type":"object","required":["src","type","rel","title","caption","copyright"],"properties":{"src":{"type":"string","format":"uri","description":"The URL of the media file. The URL must be stable and publicly accessible."},"type":{"allOf":[{"$ref":"#/components/schemas/MediaType"}],"description":"Specifies the type of the media file, which indicates its format and intended usage. Recommended types include: image/jpeg: High-quality compressed images, ideal for general use. Suggested dimensions: 1920x1080 or higher.\nimage/png: Images with transparency or higher visual fidelity, recommended for logos. Suggested dimensions: At least 1000x1000 pixels.\nvideo/mp4: Universal video format for high-quality playback. Suggested resolution: 1080p or higher.\nvideo/avi: A less common video format; MP4 is generally preferred for compatibility.\nexternal/youtube: URL links to YouTube videos for dynamic content. Use a shareable URL format.\nexternal/vimeo: URL links to Vimeo-hosted videos for high-quality or private video content."},"rel":{"allOf":[{"$ref":"#/components/schemas/MediaRel"}],"description":"Defines the relationship of the media file to the supplier's content. Common values include: LOGO: For branding assets like supplier logos.\nCOVER: For primary visual elements representing the supplier.\nGALLERY: For additional images or videos."},"title":{"type":"string","nullable":true,"description":"The title or name of the media, providing a brief description or identifier for the media file. This helps in organizing and identifying media files (e.g., \"Main Attraction Image,\" \"Promotional Video\"). This field can be null if no title is provided."},"caption":{"type":"string","nullable":true,"description":"A caption providing additional context or information about what is depicted in the media. Captions should be customer-facing and provide insights such as \"Overview of the city skyline at sunset\" or \"Guests enjoying the guided tour.\" This field can be null if no caption is provided."},"copyright":{"type":"string","nullable":true,"description":"Information about the copyright status or usage restrictions of the media. This may include details about ownership, licensing terms, or attribution requirements (e.g., \"© 2024 Example Corp, All Rights Reserved\"). If null, it is assumed there are no copyright restrictions or attribution requirements."}}},"MediaType":{"type":"string","enum":["image/jpeg","image/png","video/mp4","video/avi","external/youtube","external/vimeo"]},"MediaRel":{"type":"string","enum":["LOGO","COVER","GALLERY"]},"Location":{"type":"object","required":["title","shortDescription","types","minutesTo","minutesAt","place"],"properties":{"title":{"type":"string","nullable":true,"description":"The name of the location, providing a recognizable identifier for customers (e.g., \"Statue of Liberty\"). This field can be null if no name is available."},"shortDescription":{"type":"string","nullable":true,"description":"A brief description of the location, summarizing its significance or role in the product (e.g., \"Historic landmark and popular tourist destination\"). This field can be null if no description is provided."},"types":{"type":"array","items":{"$ref":"#/components/schemas/LocationType"},"description":"Specifies the roles or purposes of the location within the product. START: The starting point or meeting location for the product or experience. This is where customers are expected to gather before the activity begins.\nREDEMPTION: A location where customers must go to exchange tickets, collect passes, or redeem vouchers before proceeding to the starting point or experience (if applicable).\nITINERARY_ITEM: A designated stop or location within the itinerary, typically where customers pause or spend time during a moving tour or activity.\nPOINT_OF_INTEREST: A notable location or attraction that customers may see or pass by without stopping. Generally used for sightseeing locations.\nADMISSION_INCLUDED: A location where entry is included in the product price, often highlighting an attraction or event that customers can access as part of the experience.\nEND: The final point or drop-off location where the activity concludes."},"minutesTo":{"type":"integer","nullable":true,"description":"The travel time, in minutes, needed to reach this location from the previous one in the itinerary. Useful for building schedules or itineraries. Set to null if travel time is unknown, not relevant, or not required."},"minutesAt":{"type":"integer","nullable":true,"description":"The approximate duration, in minutes, spent at this location. Helps provide clarity on the itinerary or scheduling details. Set to null if the time spent is flexible, unknown, or not applicable."},"place":{"allOf":[{"$ref":"#/components/schemas/Place"}],"description":"An object containing detailed geospatial and postal address data for the location."}}},"LocationType":{"type":"string","enum":["START","ITINERARY_ITEM","POINT_OF_INTEREST","ADMISSION_INCLUDED","END","REDEMPTION"]},"Place":{"type":"object","required":["latitude","longitude","postalAddress","identifiers","sameAs"],"properties":{"latitude":{"type":"number","description":"The latitude of the location, expressed in decimal degrees. Negative values represent southern latitudes."},"longitude":{"type":"number","description":"The longitude of the location, expressed in decimal degrees. Negative values represent western longitudes."},"postalAddress":{"allOf":[{"$ref":"#/components/schemas/PostalAddress"}],"description":"Structured postal address details for the location."},"identifiers":{"allOf":[{"$ref":"#/components/schemas/Identifiers"}],"description":"A list of unique identifiers from third-party platforms (e.g., Google Maps, Yelp, Tripadvisor)."},"sameAs":{"type":"array","items":{"type":"string"},"description":"A list of URLs pointing to web pages or social media profiles for the location."}}},"PostalAddress":{"type":"object","required":["streetAddress","addressLocality","addressRegion","postalCode","addressCountry","postOfficeBoxNumber"],"properties":{"streetAddress":{"type":"string","nullable":true,"description":"The primary address line, such as a street address, P.O. box, or company name. Null if not provided."},"addressLocality":{"type":"string","nullable":true,"description":"The city or locality associated with the address."},"addressRegion":{"type":"string","nullable":true,"description":"The state, province, or region associated with the address."},"postalCode":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"addressCountry":{"type":"string","nullable":true,"description":"The postal code or ZIP code for the address."},"postOfficeBoxNumber":{"type":"string","nullable":true,"description":"The post office box number associated with the address, if applicable."}}},"Identifiers":{"type":"object","required":["googlePlaceId","applePlaceId","tripadvisorLocationId","yelpPlaceId","facebookPlaceId","foursquarePlaceId","baiduPlaceId","amapPlaceId"],"properties":{"googlePlaceId":{"type":"string","nullable":true},"applePlaceId":{"type":"string","nullable":true},"tripadvisorLocationId":{"type":"string","nullable":true},"yelpPlaceId":{"type":"string","nullable":true},"facebookPlaceId":{"type":"string","nullable":true},"foursquarePlaceId":{"type":"string","nullable":true},"baiduPlaceId":{"type":"string","nullable":true},"amapPlaceId":{"type":"string","nullable":true}},"description":"Specifies the type or source of the identifier for the location. This field defines the platform or system where the identifier is valid, allowing for seamless integration with third-party systems or mapping platforms. Common examples include:\ngooglePlaceId: A unique identifier for locations on Google Maps.\napplePlaceId: A unique identifier for locations on Apple Maps.\ntripadvisorLocationId: A unique identifier for listings on TripAdvisor.\nyelpPlaceId: A unique identifier for locations on Yelp.\nfacebookPlaceId: A unique identifier for places on Facebook.\nfoursquarePlaceId: A unique identifier for venues on Foursquare.\nbaiduPlaceId: A unique identifier for locations on Baidu Maps.\namapPlaceId: A unique identifier for locations on Amap (China-based mapping platform)."},"CategoryLabel":{"type":"string","enum":["multi-day","city-cards","adults-only","animals","audio-guide","beaches","bike-tours","boat-tours","classes","day-trips","family-friendly","fast-track","food","guided-tours","history","hop-on-hop-off","literature","live-music","museums","nightlife","outdoors","private-tours","romantic","recurring-events","self-guided","small-group-tours","sports","theme-parks","walking-tours","wheelchair-accessible","accommodation-included","trip-difficulty-easy","trip-difficulty-medium","trip-difficulty-hard"]},"Commentary":{"type":"object","required":["format","language"],"properties":{"format":{"allOf":[{"$ref":"#/components/schemas/CommentaryFormat"}],"description":"Specifies the format in which commentary is provided. Possible values are:\nIN_PERSON: Live commentary delivered by a guide or host during the activity. Examples include a tour guide providing real-time explanations about historical landmarks or itinerary highlights.\nRECORDED_AUDIO: Pre-recorded audio commentary accessible during the activity. Delivered via headphones, mobile apps, or speaker systems, covering key details in multiple languages.\nWRITTEN: Commentary provided as written material, such as printed brochures, guidebooks, or on-site informational displays at points of interest.\nOTHER: Commentary formats not explicitly listed, such as augmented reality experiences or interactive digital guides."},"language":{"type":"string","description":"Specifies the language in which the commentary is offered, adhering to IETF BCP 47 language tags for compatibility."}}},"CommentaryFormat":{"type":"string","enum":["IN_PERSON","RECORDED_AUDIO","WRITTEN","OTHER"]},"PricingPer":{"type":"string","enum":["BOOKING","UNIT"]},"BookingCancellation":{"type":"object","required":["refund","reason","utcCancelledAt"],"properties":{"refund":{"allOf":[{"$ref":"#/components/schemas/Refund"}],"description":"Whether the booking was refunded as part of the cancellation. Possible values are FULL, PARTIAL or NONE"},"reason":{"type":"string","nullable":true,"description":"A text value describing why the cancellation happened."},"utcCancelledAt":{"type":"string","format":"date-time","description":"An ISO8601 date time in UTC indicating when the booking was cancelled."}}},"Refund":{"type":"string","enum":["FULL","PARTIAL","NONE"]},"Availability":{"type":"object","required":["id","localDateTimeStart","localDateTimeEnd","utcCutoffAt","allDay","available","status","vacancies","capacity","maxUnits","openingHours"],"properties":{"id":{"type":"string","description":"A unique identifier for this availability. This ID is used during booking and must be unique within the scope of an option."},"localDateTimeStart":{"type":"string","description":"The start time for this availability in the product’s local time zone. This value must conform to ISO 8601 standards (e.g., \"2024-11-17T09:00:00+00:00\")."},"localDateTimeEnd":{"type":"string","description":"The end time for this availability in the product’s local time zone. It must also adhere to ISO 8601 standards."},"utcCutoffAt":{"type":"string","format":"date-time","description":"The time by which the booking must be confirmed at"},"allDay":{"type":"boolean","description":"Indicates if this availability spans the entire day. If set to true, there will be no specific start or end times for this availability."},"available":{"type":"boolean","description":"Indicates if there are remaining slots available for this date or time slot."},"status":{"allOf":[{"$ref":"#/components/schemas/AvailabilityStatus"}],"description":"Defines the current status of the availability:\nAVAILABLE: Open for booking.\nFREESALE: Unlimited availability, no capacity limits.\nSOLD_OUT: No spots available.\nLIMITED: Less than 50% capacity remaining.\nCLOSED: The availability is closed."},"vacancies":{"type":"integer","nullable":true,"description":"Specifies the number of available slots remaining. Should be nulled or omitted when status is FREESALE. If availability is tracked per unit, this represents the maximum remaining quantity across all units."},"capacity":{"type":"integer","nullable":true,"description":"The total capacity for this availability."},"maxUnits":{"type":"integer","nullable":true,"description":"The maximum number of units that can be sold in a single booking during this availability slot."},"openingHours":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHours"},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"unitPricing":{"type":"array","items":{"$ref":"#/components/schemas/PricingUnit"},"description":"Is on the object when Pricing capability is requested. "},"pricing":{"type":"array","items":{"$ref":"#/components/schemas/Pricing"},"description":"Is on the object when Pricing capability is requested. "},"title":{"type":"string","nullable":true,"description":"The public, customer-facing for the availablity. This name is displayed to end customers and should accurately represent the option for marketing and sales purposes. Can be null when not appliable "},"shortDescription":{"type":"string","description":"A brief, customer-facing description of the availability. This field provides a concise overview of availability. "}}},"AvailabilityStatus":{"type":"string","enum":["AVAILABLE","FREESALE","SOLD_OUT","LIMITED","CLOSED"]},"OpeningHours":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","description":"The opening time"},"to":{"type":"string","description":"The closing time"}},"description":"Defines the opening hours for this availability, even for start time-based availability. Supports multiple periods for breaks in the day."},"PricingUnit":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"ID of the unit this pricing is related to"}},"allOf":[{"$ref":"#/components/schemas/Pricing"}]},"Contact":{"type":"object","required":["fullName","firstName","lastName","emailAddress","phoneNumber","locales","postalCode","country","notes"],"properties":{"fullName":{"type":"string","nullable":true,"description":"The full name of the booking holder. Can also be retrieved as an alias for the concatenation of firstName and lastName"},"firstName":{"type":"string","nullable":true,"description":"The first name of the booking holder."},"lastName":{"type":"string","nullable":true,"description":"The last name of the booking holder."},"emailAddress":{"type":"string","nullable":true,"format":"email","description":"The email address of the booking holder."},"phoneNumber":{"type":"string","nullable":true,"description":"The phone number of the booking holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment; representing customer language for booking communications."},"postalCode":{"type":"string","nullable":true,"description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","nullable":true,"description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","nullable":true,"description":"Customer-facing public notes for the booking."}}},"Ticket":{"type":"object","required":["redemptionMethod","utcRedeemedAt","deliveryOptions"],"properties":{"redemptionMethod":{"allOf":[{"$ref":"#/components/schemas/RedemptionMethod"}],"description":"Specifies how the voucher can be redeemed by the customer:\nDIGITAL: The voucher must be presented, either scanned from a digital device (e.g., smartphone) or as a printed copy. Redemption requires a valid voucher or ticket, even in digital form.\nMANIFEST: The customer’s name, reference, or other information is checked against a manifest by the supplier. Redemption does not require a ticket or voucher.\nPRINT: A physical printed voucher is strictly required for redemption and must be presented at the time of use.\nThis field ensures resellers and customers understand the specific requirements for redeeming this booking."},"utcRedeemedAt":{"type":"string","nullable":true,"description":"An ISO8601 date time in UTC at when the voucher was redeemed, if applicable."},"deliveryOptions":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryOption"},"description":"All possible delivery options supplier accepts, in the order of supplier preference"}}},"DeliveryOption":{"type":"object","required":["deliveryFormat","deliveryValue"],"properties":{"deliveryFormat":{"allOf":[{"$ref":"#/components/schemas/DeliveryFormat"}],"description":"The format in which vouchers for this product are delivered. Each format specifies how the vouchers will be represented:\nQRCODE: A code presented as a QR Code, commonly used for scanning at entry points.\nCODE128: A linear barcode format widely used for retail and ticketing purposes.\nAZTECCODE: A two-dimensional barcode format similar to QR codes but more compact. It is optimized for small spaces and often used in transportation and event ticketing.\nPDF_URL: A URL linking to a downloadable PDF containing the complete ticket details for this product.\nPKPASS_URL: A URL for adding the ticket to Apple Wallet (Passbook) for easy access on iOS devices.\nThis field ensures resellers can understand and integrate the appropriate ticket delivery formats specifically associated with this product.\""},"deliveryValue":{"type":"string","description":"The string with the value of the delivery option, e.g. value behind the QRCODE, CODE128, AZTECCODE, or URL hosting the file for PDF_URL or PKPASS_URL)"}}},"UnitItem":{"type":"object","required":["uuid","resellerReference","supplierReference","unitId","status","utcRedeemedAt","contact","ticket"],"properties":{"uuid":{"type":"string","description":"The id of the unit, this will be unique to the option."},"resellerReference":{"type":"string","nullable":true,"description":"A reference the reseller uses to identify the unit within all bookings."},"supplierReference":{"type":"string","nullable":true,"description":"A reference the supplier uses to identify the unit within all bookings."},"unitId":{"type":"string","description":"This MUST be a unique identifier within the scope of the option."},"unit":{"allOf":[{"$ref":"#/components/schemas/Unit"}],"description":""},"status":{"allOf":[{"$ref":"#/components/schemas/BookingStatus"}],"description":"The status of the booking, possible values are:\n`ON_HOLD` The booking is pending confirmation, this is the default value when you first create the booking.\n`EXPIRED` If the booking is not confirmed before the expiration hold expires, it goes into an expired state.\n`CONFIRMED` Once the confirmation call is made the booking is ready to be used.\n`CANCELLED` If the booking is cancelled.\n`PENDING` If the booking is pending outside availability confirmation.\n`REDEEMED` If the booking is already redeemed."},"utcRedeemedAt":{"type":"string","format":"date-time","nullable":true,"description":"The ISO8601 date in UTC indicating when the ticket was used at the attraction."},"contact":{"allOf":[{"$ref":"#/components/schemas/Contact"}],"description":"Contact details for the guests that will attend the tour/attraction. Contact Body can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)"},"ticket":{"type":"object","allOf":[{"$ref":"#/components/schemas/Ticket"}],"nullable":true,"description":""},"pricing":{"allOf":[{"$ref":"#/components/schemas/Pricing"}],"description":"Is on the object when Pricing capability is requested. "}}},"ErrorInvalidProductID":{"type":"object","required":["productId"],"properties":{"productId":{"type":"string","description":"Missing or invalid `productId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BaseError":{"type":"object","required":["error","errorMessage"],"properties":{"error":{"type":"string","description":"The error code. A table of possible error codes is shown below."},"errorMessage":{"type":"string","description":"A human-readable error message will be translated depending on the language provided by the Accept-Language header."}}},"ErrorInvalidOptionID":{"type":"object","required":["optionId"],"properties":{"optionId":{"type":"string","description":"Missing or invalid `optionId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidUnitID":{"type":"object","required":["unitId"],"properties":{"unitId":{"type":"string","description":"Missing or invalid `unitId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidAvailabilityID":{"type":"object","required":["availabilityId"],"properties":{"availabilityId":{"type":"string","description":"Missing or invalid `availabilityId` in the request"}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInvalidBookingUUID":{"type":"object","required":["uuid"],"properties":{"uuid":{"type":"string","description":"Missing or invalid booking UUID, or if you're confirming the booking the booking may have expired already."}},"allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnprocessableEntity":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorUnauthorized":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorInternalServerError":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"ErrorForbidden":{"type":"object","allOf":[{"$ref":"#/components/schemas/BaseError"}]},"BookingUpdateBody":{"type":"object","properties":{"resellerReference":{"type":"string","description":"Your reference for this booking. Also known as a Voucher Number."},"productId":{"type":"string","description":"The product ID."},"optionId":{"type":"string","description":"The option id."},"availabilityId":{"type":"string","description":"The availability ID for the selected timeslot."},"expirationMinutes":{"type":"integer","description":"How many minutes to reserve the availability, otherwise defaults to the supplier default amount."},"notes":{"type":"string","description":"Optional notes for the booking."},"emailReceipt":{"type":"boolean","description":"Whether you want OCTO Cloud to email the guest a copy of their receipt and tickets. (defaults to false)."},"unitItems":{"type":"array","items":{"$ref":"#/components/schemas/BookingUnitItem"},"description":"An array of unit items in the booking. To retain or modify existing unit items, you must include the unit item with the associated uuid, otherwise that unit item will be removed."},"contact":{"allOf":[{"$ref":"#/components/schemas/BookingContact"}],"description":"Contact details for the main guest who will attend the tour/attraction. Contact BODY can be applied to both the booking object (the main reservation) or the unit object (individual ticket holders - if the supplier requires this information)."}}},"BookingUnitItem":{"type":"object","required":["unitId"],"properties":{"uuid":{"type":"string","format":"uuid","description":"The unit item unit ID."},"unitId":{"type":"string","description":"A unique UUID to identify the unit, same as the booking uuid except per unit."}}},"BookingContact":{"type":"object","properties":{"fullName":{"type":"string","description":"The full name of the booking holder or the ticket holder. Can also be retrieved as an alias for the concatenation of `firstName` and `lastName`"},"firstName":{"type":"string","description":"The first name of the booking holder or the ticket holder."},"lastName":{"type":"string","description":"The last name of the booking holder or the ticket holder."},"emailAddress":{"type":"string","format":"email","description":"The email address of the booking holder or the ticket holder."},"phoneNumber":{"type":"string","description":"The phone number of the booking holder or the ticket holder."},"locales":{"type":"array","items":{"type":"string"},"description":"An array of locale values, equivalent to navigator.languages in a browsers environment."},"postalCode":{"type":"string","description":"The PO Box of the booking holder or the ticket holder."},"country":{"type":"string","description":"The country of the booking holder or the ticket holder."},"notes":{"type":"string","description":"Optional notes for the booking."}}}}},"paths":{"/bookings/{uuid}":{"patch":{"operationId":"Bookings_BookingUpdate","summary":"Booking Update","description":"Updates a booking before and after it has been confirmed as long as it hasn''t been redeemed or within the cancellation cutoff window. To know if the booking can be updated check the booking''s `cancellable` field. If the booking can be cancelled, it can also be updated. It''s generally preferred to update a booking rather than cancelling it and rebooking","parameters":[{"$ref":"#/components/parameters/BookingUpdateRequest.uuid"},{"$ref":"#/components/parameters/RequestHeaders.octoCapabilities"},{"$ref":"#/components/parameters/RequestHeadersContent"}],"responses":{"200":{"description":"The request has succeeded.","headers":{"Octo-Capabilities":{"required":true,"description":"A list of the Capabilities (their IDs) initialized with your request.","schema":{"type":"string"}},"Content-Language":{"required":false,"description":"This response header indicates the language of the content being returned in the response. The OCTO specification allows only one language to be returned per response. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  To obtain content in multiple languages, separate requests must be made for each desired language. This header is defined in the HTTP/1.1 specification (RFC 7231). For more information, see MDN Web Docs: Content-Language - HTTP | MDN. This response header is required when using Content capability.","schema":{"type":"string"}},"Available-Languages":{"required":false,"description":"This response header is used to inform of the languages in which content is available, helping understand the language options without needing additional requests. This code must conform to the BCP 47 standard, following RFC 5646 and RFC 4647 specifications for language tags. Examples include en-US for American English, fr-FR for French (France), and es-ES for Spanish (Spain).  Although not a standard HTTP header, it is commonly used in APIs to list available languages, such as en-US, fr-CA, es-ES, indicating that content can be requested in U.S. English, Canadian French, or Spanish. This response header is required when using Content capability.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"The server could not understand the request due to invalid syntax.","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErrorInvalidProductID"},{"$ref":"#/components/schemas/ErrorInvalidOptionID"},{"$ref":"#/components/schemas/ErrorInvalidUnitID"},{"$ref":"#/components/schemas/ErrorInvalidAvailabilityID"},{"$ref":"#/components/schemas/ErrorInvalidBookingUUID"},{"$ref":"#/components/schemas/ErrorUnprocessableEntity"},{"$ref":"#/components/schemas/ErrorUnauthorized"},{"$ref":"#/components/schemas/ErrorInternalServerError"},{"$ref":"#/components/schemas/ErrorForbidden"}]}}}}},"tags":["Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdateBody"}}}}}}}}
```


# Pricing

Adds pricing fields to various endpoints

To use this capability, add `pricing` to your `Octo-Capabilities` header. This capability adds pricing to most endpoints, giving you advanced static and dynamic pricing capabilities.

## Product Pricing

<mark style="color:blue;">`GET`</mark> `{host}/products/:id`

Returns top-level pricing by unit on each product.

#### Path Parameters

| Name | Type   | Description                                          |
| ---- | ------ | ---------------------------------------------------- |
| id   | string | The product id, leave this out to get every product. |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "id": "93c335bc-07b9-4f10-84e2-3df8f2daa257",
  "internalName": "Mega Pass",
  "reference": null,
  "locale": "en",
  "timeZone": "America/Los_Angeles",
  "allowFreesale": false,
  "availabilityRequired": true,
  "availabilityType": "OPENING_HOURS",
  "deliveryFormats": [
    "PDF_URL",
    "QRCODE"
  ],
  "deliveryMethods": [
    "VOUCHER",
    "TICKET"
  ],
  "redemptionMethod": "DIGITAL",
  "options": [
    {
      "id": "6963c6a3-5d6a-4f15-924c-be2530589422",
      "default": false,
      "internalName": "Pick 3",
      "reference": null,
      "restrictions": {
        "minUnits": 0,
        "maxUnits": null
      },
      "units": [
        {
          "id": "adult",
          "internalName": "Adult",
          "reference": "adult",
          "type": "ADULT",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 7999,
              "retail": 7999,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        },
        {
          "id": "child",
          "internalName": "Child",
          "reference": "child",
          "type": "CHILD",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 5999,
              "retail": 5999,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        }
      ]
    },
    {
      "id": "f39bde2f-2cc0-48c1-b404-af68ce2370ae",
      "default": false,
      "internalName": "Pick 4",
      "reference": null,
      "restrictions": {
        "minUnits": 0,
        "maxUnits": null
      },
      "units": [
        {
          "id": "adult",
          "internalName": "Adult",
          "reference": "adult",
          "type": "ADULT",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 9999,
              "retail": 9999,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        },
        {
          "id": "child",
          "internalName": "Child",
          "reference": "child",
          "type": "CHILD",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 7999,
              "retail": 7999,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        }
      ]
    },
    {
      "id": "f996c316-7541-4cb4-a96f-4a6f08cea70a",
      "default": false,
      "internalName": "Pick 5",
      "reference": null,
      "restrictions": {
        "minUnits": 0,
        "maxUnits": null
      },
      "units": [
        {
          "id": "adult",
          "internalName": "Adult",
          "reference": "adult",
          "type": "ADULT",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 11499,
              "retail": 11499,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        },
        {
          "id": "child",
          "internalName": "Child",
          "reference": "child",
          "type": "CHILD",
          "restrictions": {
            "minAge": 0,
            "maxAge": 99,
            "idRequired": false,
            "minQuantity": null,
            "maxQuantity": null,
            "paxCount": 1,
            "accompaniedBy": []
          },
          "pricingFrom": [
            {
              "original": 9299,
              "retail": 9299,
              "net": null,
              "currency": "USD",
              "currencyPrecision": 2
            }
          ]
        }
      ]
    }
  ],
  "defaultCurrency": "USD",
  "availableCurrencies": [
    "USD"
  ]
}
```

{% endtab %}
{% endtabs %}

On the response, the only changes from the original schema is on the Product object:

```javascript
{
  // ...rest of Product object
  "defaultCurrency": "USD",
  "availableCurrencies": ["USD", "EUR", "GBP"],
  "pricingPer": "UNIT" // "UNIT" or "BOOKING"
}
```

The `defaultCurrency` is the default currency for this product, if you omit the `currency` parameter on future endpoints this is the value the reservation system will fallback to. `availableCurrencies` are all the possible currencies that we accept for this product.

`pricingPer` indicates whether the pricing is per unit (most common), or per booking. Pricing which is per booking is common for private charters or group booking products where the price is the same regardless of how many tickets are purchased.

Next, if `pricingPer = "UNIT"`, on each Unit (adult, child, etc.) we add the following:

```javascript
{
  // ...rest of the Unit object
  "pricingFrom": [
    {
      "original": 4500,
      "retail": 4500,
      "net": 3500,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    },
    {
      "original": 4000,
      "retail": 4000,
      "net": 3000,
      "currency": "GBP",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 700,
          "net": 400
        }
      ]
    }
    // ...etc...
  ]
}
```

If `pricingPer = "BOOKING"` then these fields will be on the Product itself instead of the Unit as the pricing applies once to the booking regardless of how many units (tickets) are purchased.

We'll produce one `pricingFrom` object for each currency in `availableCurrencies` . The meaning of each pricing field is given below:

| Field               | Description                                                                                                                                                                                                                                                |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `original`          | The original price for this product which will be the same or higher than the sale amount. Use this to show a discount has been applied e.g. ~~$10~~ **$8.50**                                                                                             |
| `retail`            | The sale price you should charge your customers.                                                                                                                                                                                                           |
| `net`               | The wholesale rate the supplier will charge you for this sale.                                                                                                                                                                                             |
| `currency`          | The currency.                                                                                                                                                                                                                                              |
| `currencyPrecision` | All pricing is given in integers to avoid floating point rounding issues. e.g. USD = 2 and JPY = 0. To convert a price to decimal you should do: `price / (10 ** currencyPrecision)` where \*\* is to the power of e.g. `Math.pow(10, currencyPrecision)`. |
| `includedTaxes`     | Any taxes included in the retail and/or net price.                                                                                                                                                                                                         |

{% hint style="warning" %}
Throughout this capability, we'll use a convention where we'll end the object key with `From` to indicate this is indicative and not the final price. Make sure you communicate this also to the customer.
{% endhint %}

## Pricing Calendar

<mark style="color:green;">`POST`</mark> `{host}/availability/calendar`

Returns pricing per day for when you're generating a calendar view.

#### Request Body

| Name     | Type   | Description         |
| -------- | ------ | ------------------- |
| currency | string | The currency to use |

{% tabs %}
{% tab title="200 " %}

```javascript
[
  //.. each day
  {
    "localDate": "2020-07-01",
    "status": "AVAILABLE",
    "capacity": 24,
    "openingHours": []
    "unitPricingFrom": [
      {
        "unitId": "adult",
        "original": 4500,
        "retail": 4500,
        "net": 3500,
        "currency": "USD",
        "currencyPrecision": 2
      },
      {
        "unitId": "child",
        "original": 4200,
        "retail": 4200,
        "net": 3200,
        "currency": "USD",
        "currencyPrecision": 2
      }
    ]
  }
]
```

{% endtab %}
{% endtabs %}

The documentation above only shows the additions this capability adds to the availability calendar endpoint. See the documentation [here](broken://pages/MaunWfuPk3q2xvHC1yxl) to see the full request parameters and response object.

Each availability object is given a new `unitPricingFrom` field with an array of unit prices in the currency for example:

{% tabs %}
{% tab title="pricingPer = UNIT" %}

```javascript
{
  "localDate": "2020-07-01",
  "status": "AVAILABLE",
  "capacity": 24,
  "openingHours": []
  "unitPricingFrom": [
    {
      "unitId": "unit_adult123",
      "original": 4500,
      "retail": 4500,
      "net": 3500,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 700,
          "net": 400
        }
      ]
    },
    {
      "unitId": "unit_child321",
      "original": 4200,
      "retail": 4200,
      "net": 3200,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  ]
}
```

{% endtab %}

{% tab title="pricingPer = BOOKING" %}

```javascript
{
  "localDate": "2020-07-01",
  "status": "AVAILABLE",
  "capacity": 24,
  "openingHours": []
  "pricingFrom": [
    {
      "original": 4500,
      "retail": 4500,
      "net": 3500,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 700,
          "net": 400
        }
      ]
    },
    {
      "original": 4200,
      "retail": 4200,
      "net": 3200,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

If you pass the `units` to the request then we'll give the total pricing for the selection under the `pricingFrom` field, which is also included by default if `pricingPer = BOOKING` as the number of units isn't needed to know what the price of the booking is. For example:

{% tabs %}
{% tab title="Request" %}

```javascript
{
  "productId": "1a7213eb-3a33-4cbb-b114-64d771c201ac",
  "optionId": "DEFAULT",
  "localDateStart": "2020-07-01",
  "localDateEnd": "2020-07-02",
  "units": [
    {
      "id": "unit_adult123",
      "quantity": 2
    },
    {
      "id": "unit_child321",
      "quantity": 1
    }
  ]
}
```

{% endtab %}

{% tab title="Response (pricingPer = UNIT)" %}

```javascript
[
  {
    "localDate": "2020-07-01",
    "status": "AVAILABLE",
    "capacity": 24,
    "openingHours": [],
    "unitPricingFrom": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 400,
            "net": 250
          }
        ]
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 200,
            "net": 50
          }
        ]
      }
    ],
    "pricingFrom": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  }
]
```

{% endtab %}

{% tab title="Response (pricingPer = BOOKING)" %}

```javascript
[
  {
    "localDate": "2020-07-01",
    "status": "AVAILABLE",
    "capacity": 24,
    "openingHours": [],
    "pricingFrom": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  }
]
```

{% endtab %}
{% endtabs %}

Having the `pricingFrom` value calculated for you makes it much easier to display a single price on each date on the calendar (assuming the guest has chosen how many units they want before you display the calendar).

If pricingPer = BOOKING then unitPricingFrom will not be provided.

## Pricing Check

<mark style="color:green;">`POST`</mark> `{host}/availability`

Returns a final quote of the price before making a booking.

#### Request Body

| Name     | Type   | Description         |
| -------- | ------ | ------------------- |
| currency | string | The currency to use |

{% tabs %}
{% tab title="200 " %}

```javascript
[
  {
    "id": "2020-07-01T11:30:00-05:00",
    "localDateTimeStart": "2020-07-01T11:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T23:30:00-05:00",
    "utcCutoffAt": "2020-07-01T16:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T12:00:00-05:00",
    "localDateTimeStart": "2020-07-01T12:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:00:00-05:00",
    "utcCutoffAt": "2020-07-01T17:00:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currencyPrecision": 2,
      "currency": "USD"
    }
  },
  {
    "id": "2020-07-01T14:30:00-05:00",
    "localDateTimeStart": "2020-07-01T14:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T02:30:00-05:00",
    "utcCutoffAt": "2020-07-01T19:00:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T15:00:00-05:00",
    "localDateTimeStart": "2020-07-01T15:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T03:00:00-05:00",
    "utcCutoffAt": "2020-07-01T20:00:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T10:30:00-05:00",
    "localDateTimeStart": "2020-07-01T10:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T22:30:00-05:00",
    "utcCutoffAt": "2020-07-01T15:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T13:30:00-05:00",
    "localDateTimeStart": "2020-07-01T13:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T01:30:00-05:00",
    "utcCutoffAt": "2020-07-01T15:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T09:30:00-05:00",
    "localDateTimeStart": "2020-07-01T09:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T21:30:00-05:00",
    "utcCutoffAt": "2020-07-01T14:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T12:30:00-05:00",
    "localDateTimeStart": "2020-07-01T12:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:30:00-05:00",
    "utcCutoffAt": "2020-07-01T17:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "adult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "child"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  }
]
```

{% endtab %}
{% endtabs %}

This endpoint is the availability check endpoint which has been extended to add pricing. We only document the added parameters here, to see the full documentation for the original availability check you can find it [here](broken://pages/-M94WSx-ghiLAAuYgpTC#availability-check).

If we were to repeat the request above in the calendar section this is what the response would look like instead:

{% tabs %}
{% tab title="Request" %}

```javascript
{
  "productId": "1a7213eb-3a33-4cbb-b114-64d771c201ac",
  "optionId": "DEFAULT",
  "localDateStart": "2020-07-01",
  "localDateEnd": "2020-07-02",
  "units": [
    {
      "id": "unit_123abcadult",
      "quantity": 2
    },
    {
      "id": "unit_321abcchild",
      "quantity": 1
    }
  ]
}
```

{% endtab %}

{% tab title="Response (pricingPer = UNIT)" %}

```javascript
[
  {
    "id": "2020-07-01T11:30:00-05:00",
    "localDateTimeStart": "2020-07-01T11:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T23:30:00-05:00",
    "utcCutoffAt": "2020-07-01T16:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 400,
            "net": 250
          }
        ]
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 200,
            "net": 50
          }
        ]
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  },
  {
    "id": "2020-07-01T12:00:00-05:00",
    "localDateTimeStart": "2020-07-01T12:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:00:00-05:00",
    "utcCutoffAt": "2020-07-01T17:00:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 400,
            "net": 250
          }
        ]
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild",
        "includedTaxes": [
          {
            "name": "VAT 10",
            "retail": 200,
            "net": 50
          }
        ]
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  },
  {
    "id": "2020-07-01T14:30:00-05:00",
    "localDateTimeStart": "2020-07-01T14:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T02:30:00-05:00",
    "utcCutoffAt": "2020-07-01T19:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T15:00:00-05:00",
    "localDateTimeStart": "2020-07-01T15:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T03:00:00-05:00",
    "utcCutoffAt": "2020-07-01T20:00:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T10:30:00-05:00",
    "localDateTimeStart": "2020-07-01T10:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T22:30:00-05:00",
    "utcCutoffAt": "2020-07-01T15:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T13:30:00-05:00",
    "localDateTimeStart": "2020-07-01T13:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T01:30:00-05:00",
    "utcCutoffAt": "2020-07-01T18:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T09:30:00-05:00",
    "localDateTimeStart": "2020-07-01T09:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T21:30:00-05:00",
    "utcCutoffAt": "2020-07-01T14:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T12:30:00-05:00",
    "localDateTimeStart": "2020-07-01T12:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:30:00-05:00",
    "utcCutoffAt": "2020-07-01T17:30:00Z",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "unitPricing": [
      {
        "original": 3995,
        "retail": 3995,
        "net": 2996,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_123abcadult"
      },
      {
        "original": 1995,
        "retail": 1995,
        "net": 1496,
        "currency": "USD",
        "currencyPrecision": 2,
        "unitId": "unit_321abcchild"
      }
    ],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  }
]
```

{% endtab %}

{% tab title="Response (pricingPer = BOOKING)" %}

```javascript
[
  {
    "id": "2020-07-01T11:30:00-05:00",
    "localDateTimeStart": "2020-07-01T11:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T23:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  },
  {
    "id": "2020-07-01T12:00:00-05:00",
    "localDateTimeStart": "2020-07-01T12:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:00:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2,
      "includedTaxes": [
        {
          "name": "VAT 10",
          "retail": 800,
          "net": 500
        }
      ]
    }
  },
  {
    "id": "2020-07-01T14:30:00-05:00",
    "localDateTimeStart": "2020-07-01T14:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T02:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T15:00:00-05:00",
    "localDateTimeStart": "2020-07-01T15:00:00-05:00",
    "localDateTimeEnd": "2020-07-02T03:00:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T10:30:00-05:00",
    "localDateTimeStart": "2020-07-01T10:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T22:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T13:30:00-05:00",
    "localDateTimeStart": "2020-07-01T13:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T01:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T09:30:00-05:00",
    "localDateTimeStart": "2020-07-01T09:30:00-05:00",
    "localDateTimeEnd": "2020-07-01T21:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  },
  {
    "id": "2020-07-01T12:30:00-05:00",
    "localDateTimeStart": "2020-07-01T12:30:00-05:00",
    "localDateTimeEnd": "2020-07-02T00:30:00-05:00",
    "allDay": false,
    "status": "AVAILABLE",
    "vacancies": 24,
    "capacity": 24,
    "maxUnits": 24,
    "openingHours": [],
    "pricing": {
      "original": 9985,
      "retail": 9985,
      "net": 7488,
      "currency": "USD",
      "currencyPrecision": 2
    }
  }
]
```

{% endtab %}
{% endtabs %}

Notice how the response fields are `unitPricing` and `pricing` (without the From suffix). That is because this is the final price, and this is what the booking will be once confirmed.

## Booking Reservation

<mark style="color:green;">`POST`</mark> `{host}/bookings`

The booking reservation call

#### Request Body

| Name     | Type   | Description         |
| -------- | ------ | ------------------- |
| currency | string | The currency to use |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "uuid": "4de58057-e7bd-4ec0-be9b-08eb674098b9",
  "testMode": true,
  "resellerReference": null,
  "supplierReference": "DV2JG2",
  "status": "ON_HOLD",
  "utcExpiresAt": "2020-06-08T07:32:24Z",
  "utcConfirmedAt": null,
  "productId": "8d7519d2-cac2-4daa-8aab-d9f97265e495",
  "optionId": "23df0f5c-781a-4bee-9dae-2355bdd792c5",
  "cancellable": true,
  "cancellation": null,
  "freesale": false,
  "availability": {
    "id": "2020-06-27T00:00:00+02:00",
    "localDateTimeStart": "2020-06-27T00:00:00+02:00",
    "localDateTimeEnd": "2020-06-28T00:00:00+02:00",
    "allDay": true,
    "openingHours": []
  },
  "contact": {
    "fullName": null,
    "emailAddress": null,
    "phoneNumber": null,
    "locales": [],
    "country": null,
    "notes": null
  },
  "notes": null,
  "deliveryMethods": [
    "VOUCHER",
    "TICKET"
  ],
  "voucher": {
    "redemptionMethod": "DIGITAL",
    "utcRedeemedAt": null,
    "deliveryOptions": []
  },
  "unitItems": [
    {
      "uuid": "4c4a52cd-c0a5-49d9-b1e7-a713564993d4",
      "resellerReference": null,
      "supplierReference": "VD8KTS",
      "unitId": "unit_321abcchild",
      "ticket": {
        "redemptionMethod": "DIGITAL",
        "utcRedeemedAt": null,
        "deliveryOptions": []
      }
    },
    {
      "uuid": "8d4e198d-e337-465c-b17c-e3a55798ee97",
      "resellerReference": null,
      "supplierReference": "ENV98P",
      "unitId": "unit_123abcadult",
      "ticket": {
        "redemptionMethod": "DIGITAL",
        "utcRedeemedAt": null,
        "deliveryOptions": []
      }
    },
    {
      "uuid": "f3183108-32f4-4816-99e6-b04ae40dd433",
      "resellerReference": null,
      "supplierReference": "43FJJ4",
      "unitId": "unit_123abcadult",
      "ticket": {
        "redemptionMethod": "DIGITAL",
        "utcRedeemedAt": null,
        "deliveryOptions": []
      }
    }
  ],
  "pricing": {
    "original": 8800,
    "retail": 8800,
    "net": 5500,
    "currency": "EUR",
    "currencyPrecision": 2,
    "includedTaxes": [
      {
        "name": "VAT 10",
        "retail": 800,
        "net": 500
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

This capability extends the booking schema to add a `pricing` field which gives you the final price of the booking as well as any included taxes. The final price includes tax, and should be what you display to the guest as the amount they need to pay.

```javascript
  "pricing": {
    "original": 8800,
    "retail": 8800,
    "net": 5500,
    "currency": "EUR",
    "currencyPrecision": 2,
    "includedTaxes": [
      {
        "name": "VAT 10",
        "retail": 800,
        "net": 500
      }
    ]
  }
```

We include the net amount as well as any taxes included in the net price.


# Notifications

Notifications on changes to product, availability or booking

To use this capability, add `notifications` to your `Octo-Capabilities` header. This capability allows you to subscribe to be notified when something changes against either of the:

1. **Product**
2. **Availability**
3. **Booking**

## Managing Notification Subscriptions

You can subscribe to notifications by creating subscriptions, which can be [created](#create-notification), [updated](#update-notification) or [deleted](#delete-notification). You can also [list](#list-notifications) your subscriptions or [retrieve](#get-notification) a specific one.

### Create Subscription

<mark style="color:green;">`POST`</mark> `/notifications/subcriptions`

**Headers**

| Name              | Value              |
| ----------------- | ------------------ |
| Content-Type      | `application/json` |
| Authorization     | `Bearer <token>`   |
| Octo-Capabilities | `notifications`    |

**Body**

<table><thead><tr><th width="246">Name</th><th width="107">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>url</code></td><td>string</td><td>The URL where you want the  notifications to be sent to</td></tr><tr><td><code>notificationTypes</code></td><td>array</td><td><p>The type(s) of notifications you would like to be subscribed to as a part of this subscription. Possible events are:</p><ol><li><code>PRODUCT_UPDATE</code> (changes to product object, including option, ticket, etc. within it)</li><li><code>AVAILABILITY_UPDATE</code> (changes to availabilities)</li><li><code>BOOKING_UPDATE</code> (changes to bookings)</li></ol></td></tr><tr><td><code>headers</code></td><td>object</td><td>List of HTTP headers you want to be included in the request (eg. for authentication)</td></tr></tbody></table>

#### Example Request

```json
POST /notifications/subcriptions
Content-Type: application/json

{
  "url": "https://example.com/myapp/example1",
  "notificationTypes": [
    "PRODUCT_UPDATE",
    "AVAILABILITY_UPDATE",
    "BOOKING_UPDATE"
  ],
  "headers": {
    "Api-Key": "secret"
  }
}
```

**Example Response**

```json
{
  "id": "5c7d6dbb-cd4c-48fd-9709-0ebaa14d7a00",
  "notificationTypes": [
    "PRODUCT_UPDATE",
    "AVAILABILITY_UPDATE",
    "BOOKING_UPDATE"
  ],
  "url": "https://example.com/myapp/example1",
  "headers": {
    "Api-Key": "secret"
  }
}
```

Note the `id` of your created subscription. You can use it to manage your created notification as described below. The REST endpoints will behave as you'd expect using the same schema as above.&#x20;

### Update Subscription

Update an existing notification:

<mark style="color:yellow;">`PATCH`</mark> `/notifications/subscriptions/<id>`

### Delete Subscription

Delete an existing notification:

<mark style="color:red;">`DELETE`</mark> `/notifications/subscriptions/<id>`

### Get Subscription

Retrieve an existing notification:&#x20;

<mark style="color:blue;">`GET`</mark> `/notifications/subscriptions/<id>`

### List Subscriptions&#x20;

List all of the notifications associated with your account (Bearer Token):

<mark style="color:blue;">`GET`</mark> `/notifications/subscriptions`

## Receiving Notifications

When receiving notification you've created, the request body will not include the fully serialized object of either a product, availability or booking. Instead it'll simply provide you with the parameters needed to fetch that updated resource if you choose to. This can be done using OCTO Core [Get Product](/octo-api-core/products#products-id), [Get Booking](/octo-api-core/bookings#bookings-uuid), and [Availability](/octo-api-core/availability#availability) endpoints correspondingly. This is to reduce the volume of data sent in the request and allow the receiver of the notification to chose which capabilities they want to include when fetching the updated resource.\
\
All notifications will be HTTP POST requests and include a standard payload scheme:

```json
{
  "subscriptionId": "5c7d6dbb-cd4c-48fd-9709-0ebaa14d7a00",
  "notificationType": "PRODUCT_UPDATE",
  "data": {
    ...
  }
}
```

The value included in `data` is the content of the notification will vary based on `type`:

{% tabs %}
{% tab title="PRODUCT\_UPDATE" %}

<pre class="language-json"><code class="lang-json"><strong>{
</strong>  "id": "503eac5-bfab-4465-aad1-fc023b23cdc6",
  "subscriptionId": "5c7d6dbb-cd4c-48fd-9709-0ebaa14d7a00",
  "notificationType": "PRODUCT_UPDATE",
  "utcCreatedAt": "2024-05-07T15:47:32Z",

  "data": {
    "productId": "ff53a321-a07b-4428-b8b3-086c94fb4147"
  }
}
</code></pre>

`productId` then be used to request the updated product details using [Get Product](/octo-api-core/products#get-product).
{% endtab %}

{% tab title="AVAILABILITY\_UPDATE" %}

```json
{
  "id": "5d6e16a3-17af-4293-b3a5-406b5be5fc37",
  "subscriptionId": "5c7d6dbb-cd4c-48fd-9709-0ebaa14d7a00",
  "notificationType": "AVAILABILITY_UPDATE",
  "utcCreatedAt": "2024-05-07T15:42:37Z",
  
  "data": {
    "productId": "ff53a321-a07b-4428-b8b3-086c94fb4147",
    "optionId": "49bb9bd7-2cb2-4125-9e8b-c6efdee1e060",
    "localDateStart": "2025-01-01",
    "localDateEnd": "2025-01-07"
  }
}
```

The data object will contain request parameters compatible with any valid Availability Check request, eg. including `availabilityIds` instead of `localDateStart/localDateEnd`.

You can then retrieve change details for these using [Availability](/octo-api-core/availability) endpoints.&#x20;
{% endtab %}

{% tab title="BOOKING\_UPDATE" %}

<pre class="language-json"><code class="lang-json"><strong>{
</strong>  "id": "2a84129e-3fab-461d-adb1-b12217ef0637",
  "subscriptionId": "5c7d6dbb-cd4c-48fd-9709-0ebaa14d7a00",
  "notificationType": "BOOKING_UPDATE",
  "utcCreatedAt": "2024-05-07T15:49:31Z",

  "data": {
    "uuid": "383ef506-f632-4cc1-bdf1-f619e12e94dd"
  }
}
</code></pre>

Booking `uuid` then be used to request the updated product details using [Get Booking](/octo-api-core/bookings#bookings-uuid).
{% endtab %}
{% endtabs %}

{% hint style="info" %}
It is recommended that all notifications receive a 2xx response to confirm successful delivery and processing.
{% endhint %}


# Content

Adds extra content fields to OCTO Core object schemas on select endpoints to provide detailed descriptive information about supplier, products, options, units, booking, etc.

To use this capability, add `content` to your `Octo-Capabilities` header.

This capability extends the Supplier, Product, Option, Unit, Availability, and Booking schemas to add additional descriptive content that can be used to populate product listings as well as for various other use cases that require this information.&#x20;

## Localization

Since suppliers and their systems may offer content in different languages, this content capability supports localization through:

* the `Accept-Language` request header
* the `Content-Language` response header
* and the `Available-Languages` response header

This allows clients to request and receive content in their preferred languages, and understand what language options are available for a product.

### Request Headers

The reseller system can include language preferences in the API call to the supplier system to request content in a specific language. The `Accept-Language` header communicates which languages are preferred and in what order. This header is optional—if omitted or empty, the supplier system will fall back to their default language:

<table><thead><tr><th width="178.88671875">Header</th><th width="104.37109375">Type (Format)</th><th width="101.078125">Required/Optional</th><th width="203.98828125">Field Description</th><th>Example</th></tr></thead><tbody><tr><td><code>Accept-Language</code></td><td>string (<a href="https://en.wikipedia.org/wiki/E.164">IETF BCP 47 tag</a>)</td><td>Optional</td><td>Optional request header to specify preferred languages for response content. Supports a comma-separated list of language tags with optional <code>q</code> values to set priority. Follows BCP 47 (RFC 5646 &#x26; RFC 4647). Does not guarantee content availability in those languages. Commonly used for localization in HTTP (RFC 7231). See also: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language">MDN Docs</a>.</td><td><code>en-US</code>, <code>en-GB</code>, <code>fr-CA;q=0.8</code>, <code>fr;q=0.7</code></td></tr></tbody></table>

### Response Headers

The supplier system response may include headers that indicate the language of the returned content and the set of available language options. These help reseller systems interpret the response and determine if additional requests are needed for other languages. When the `content` capability is used, both `Content-Language` and `Available-Languages` headers must be present in the response:&#x20;

| Header                | Type (Format)                                                           | Required/Optional | Field Description                                                                                                                                                                                                                                                                                    | Example                   |
| --------------------- | ----------------------------------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| `Content-Language`    | string ([IETF BCP 47 tag](https://en.wikipedia.org/wiki/E.164))         | Required          | Indicates the language of the returned content. Only one language is allowed per response. To get multiple languages, separate requests are needed. Conforms to BCP 47 and is defined in RFC 7231. See also: [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language). | `en-GB`                   |
| `Available-Languages` | array\<string> ([IETF BCP 47 tag](https://en.wikipedia.org/wiki/E.164)) | Required          | Lists all languages in which the content is available. Not a standard HTTP header, but widely used in APIs for localization awareness. Helps determine language options without making extra requests. Must follow BCP 47 standards.                                                                 | `en-GB`, `es-ES`, `de-DE` |

### Example Headers with Localization:&#x20;

#### Request

```http
GET /octo/products/be665021-a694-4320-9e7d-6f04a7541755 HTTP/1.1
Host: api.example.com
User-Agent: MyClient/1.0.0
Accept: application/json
Authorization: Bearer 3b162482-7fe3-4d91-b6b4-26302530f0dc
Content-Type: application/json
Octo-Capabilities: content, pricing
Octo-Env: live
Accept-Language: fr-FR, fr;q=0.9, en;q=0.8
```

#### Response

```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Octo-Capabilities: content, pricing
Content-Language: fr-FR
Octo-Env: live
Octo-Available-Languages: en, fr, es, de
```

## Additional Content Fields

The `content` capability enriches standard OCTO Core objects with additional, language-specific content such as titles, descriptions, and other localized attributes.

When the capability `content` is included in the `Octo-Capabilities` header of a request to any OCTO Core endpoint, and if the supplier system supports this capability, the following objects may include additional fields:

* Supplier
* Product
* Option
* Unit
* Availability
* Booking

These enrichments are dynamically included in the response based on language preferences provided in the request (via `Accept-Language`) and the available translations on the supplier side.

Each object’s additional fields will be detailed in the following sections below:

### Supplier

The following fields are **added** to the [OCTO Core Supplier object](https://docs.octo.travel/octo-api-core/supplier) when the `content` capability is used and supported by the supplier system.

These fields provide localized, customer-facing information and rich media assets. They are returned only when:

* The supplier supports the `content` capability
* The request includes the header: `Octo-Capabilities: content`

#### Enriched Fields (from `content` Capability)

| Field              | Type             | Description                                                                                                                                        |
| ------------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shortDescription` | string \| null   | A brief, customer-facing summary of the supplier’s business. Can be localized. May be `null` if not available.                                     |
| `media`            | array of objects | A list of associated media objects such as logos, promotional images, or videos. Each object contains metadata to support display and attribution. |

**`media[]` Object Fields**

| Field       | Type           | Description                                                                                                                                                                                                  |
| ----------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type`      | string (enum)  | Format of the media. Supported types include: `image/jpeg`, `image/png`, `image/webp`, `image/svg+xml`, `video/mp4`, `video/avi`, `external/youtube`, `external/vimeo`, `external/other`, `application/pdf`. |
| `rel`       | string (enum)  | Defines the relationship of the media to the supplier. Values: `LOGO`, `COVER`, `GALLERY`.                                                                                                                   |
| `src`       | string (uri)   | The URL of the media file. Must be stable and publicly accessible.                                                                                                                                           |
| `title`     | string \| null | Title or label of the media file. Useful for organizational or display purposes.                                                                                                                             |
| `caption`   | string \| null | A customer-facing caption that provides additional context or information about the media.                                                                                                                   |
| `copyright` | string \| null | Copyright or usage restrictions, such as attribution or licensing notes. Can be `null` if no restrictions apply.                                                                                             |

***

#### Example: Supplier Object with `content` Capability Fields:

```json
{
  "id": "697e3ce8-1860-4cbf-80ad-95857df1f640", // OCTO Core field
  "endpoint": "https://api.supplierdomain.com/octo", // OCTO Core field
  "name": "Merlin Entertainments", // OCTO Core field
  "contact": { // OCTO Core field
    "website": "https://www.merlinentertainments.biz", // OCTO Core field
    "email": "info@merlinentertainments.biz", // OCTO Core field
    "telephone": "+441202666900", // OCTO Core field
    "address": "Link House, 25 West St, Poole BH15 1LD, United Kingdom" // OCTO Core field
  },
  "shortDescription": "Merlin Entertainments is a global leader in location-based entertainment, operating iconic attractions such as LEGOLAND, Madame Tussauds, SEA LIFE, and The Dungeons.", 
  "media": [ 
    {
      "type": "image/png", 
      "rel": "LOGO", 
      "src": "https://www.merlinentertainments.biz/media/logo-blue.png", 
      "title": "Merlin Entertainments Logo", 
      "caption": "Official Logo of Merlin Entertainments", 
      "copyright": "© 2024 Merlin Entertainments" 
    },
    {
      "type": "image/jpeg", 
      "rel": "COVER", 
      "src": "https://cdn.example.com/images/cover-merlin.jpg", 
      "title": "Main Visual", 
      "caption": "Families enjoying the LEGOLAND experience", 
      "copyright": null 
    },
    {
      "type": "external/youtube", 
      "rel": "GALLERY", 
      "src": "https://www.youtube.com/watch?v=xyz12345", 
      "title": "Promo Video", 
      "caption": "Discover Merlin attractions worldwide", 
      "copyright": null 
    },
    {
      "type": "external/other", 
      "rel": "GALLERY", 
      "src": "https://cdn.example.com/media/promo-pdf.pdf", 
      "title": "Brochure PDF", 
      "caption": "Download our detailed product brochure", 
      "copyright": "© 2024 Merlin Entertainments" 
    }
  ]
}
```

#### Product

The following fields are added to the [OCTO Core Product object ](https://docs.octo.travel/octo-api-core/products)when the `content` capability is supported and requested.

They enhance the product with **localized titles, descriptions, rich media, features, FAQs, category labels, durations, commentary, and location data** to help resellers provide a complete, customer-facing product view.

These fields are returned when:

* The supplier supports the `content` capability
* The request includes the header: `Octo-Capabilities: content`

#### Enriched Fields (from `content` Capability)

<table><thead><tr><th>Field</th><th width="229.55859375">Type (Format)</th><th>Description</th></tr></thead><tbody><tr><td><code>title</code></td><td>string</td><td>Public, customer-facing name of the product.</td></tr><tr><td><code>shortDescription</code></td><td>string | null</td><td>Brief marketing summary.</td></tr><tr><td><code>description</code></td><td>string | null</td><td>Long-form product narrative.</td></tr><tr><td><code>features[]</code></td><td>array</td><td>List of structured features grouped by type.</td></tr><tr><td><code>features[].shortDescription</code></td><td>string | null</td><td>Short label for the feature.</td></tr><tr><td><code>features[].type</code></td><td>string (enum)</td><td>One of: <code>INCLUSION</code>, <code>EXCLUSION</code>, <code>HIGHLIGHT</code>, <code>PREBOOKING_INFORMATION</code>, <code>PREARRIVAL_INFORMATION</code>, <code>REDEMPTION_INSTRUCTION</code>, <code>ACCESSIBILITY_INFORMATION</code>, <code>ADDITIONAL_INFORMATION</code>, <code>BOOKING_TERM</code>, <code>CANCELLATION_TERM</code>.</td></tr><tr><td><code>faqs[]</code></td><td>array</td><td>Frequently asked questions.</td></tr><tr><td><code>faqs[].question</code></td><td>string</td><td>The question text.</td></tr><tr><td><code>faqs[].answer</code></td><td>string</td><td>The corresponding answer.</td></tr><tr><td><code>media[]</code></td><td>array</td><td>List of media assets (images, PDFs, videos, etc.).</td></tr><tr><td><code>media[].src</code></td><td>string (uri)</td><td>URL of the media file.</td></tr><tr><td><code>media[].type</code></td><td>string (enum)</td><td>One of: <code>image/jpeg</code>, <code>image/png</code>, <code>image/webp</code>, <code>image/svg+xml</code>, <code>video/mp4</code>, <code>video/avi</code>, <code>external/youtube</code>, <code>external/vimeo</code>, <code>external/other</code>, <code>application/pdf</code>.</td></tr><tr><td><code>media[].rel</code></td><td>string (enum)</td><td>One of: <code>LOGO</code>, <code>COVER</code>, <code>GALLERY</code>.</td></tr><tr><td><code>media[].title</code></td><td>string | null</td><td>Title of the media.</td></tr><tr><td><code>media[].caption</code></td><td>string | null</td><td>Customer-facing caption.</td></tr><tr><td><code>media[].copyright</code></td><td>string | null</td><td>Copyright or license information.</td></tr><tr><td><code>locations[]</code></td><td>array</td><td>List of places associated with the product.</td></tr><tr><td><code>locations[].title</code></td><td>string | null</td><td>Name of the location.</td></tr><tr><td><code>locations[].shortDescription</code></td><td>string | null</td><td>Short description of the location.</td></tr><tr><td><code>locations[].types[]</code></td><td>array</td><td>One or more of: <code>START</code>, <code>ITINERARY_ITEM</code>, <code>POINT_OF_INTEREST</code>, <code>ADMISSION_INCLUDED</code>, <code>END</code>, <code>REDEMPTION</code>.</td></tr><tr><td><code>locations[].minutesTo</code></td><td>integer | null</td><td>Minutes to reach this location from the previous. Required for <code>ITINERARY_ITEM</code> or <code>END</code>.</td></tr><tr><td><code>locations[].minutesAt</code></td><td>integer | null</td><td>Minutes spent at this location. Required for <code>ITINERARY_ITEM</code> or <code>END</code>.</td></tr><tr><td><code>locations[].place</code></td><td>object</td><td>Detailed geographic and address information.</td></tr><tr><td><code>place.latitude</code></td><td>float</td><td>Latitude in decimal degrees.</td></tr><tr><td><code>place.longitude</code></td><td>float</td><td>Longitude in decimal degrees.</td></tr><tr><td><code>place.postalAddress</code></td><td>object</td><td>Postal address object.</td></tr><tr><td><code>postalAddress.streetAddress</code></td><td>string | null</td><td>Street line.</td></tr><tr><td><code>postalAddress.addressLocality</code></td><td>string | null</td><td>City.</td></tr><tr><td><code>postalAddress.addressRegion</code></td><td>string | null</td><td>Region or state.</td></tr><tr><td><code>postalAddress.postalCode</code></td><td>string | null</td><td>ZIP or postal code.</td></tr><tr><td><code>postalAddress.addressCountry</code></td><td>string | null (ISO 3166-1 alpha-2)</td><td>Two-letter country code.</td></tr><tr><td><code>postalAddress.postOfficeBoxNumber</code></td><td>string | null</td><td>PO box if available.</td></tr><tr><td><code>place.identifiers[]</code></td><td>array</td><td>Third-party platform identifiers.</td></tr><tr><td><code>identifiers.&#x3C;identifierType></code></td><td>string</td><td>Source platform (e.g., <code>googlePlaceId</code>).</td></tr><tr><td><code>identifiers.&#x3C;identifierValue></code></td><td>string</td><td>ID value from the source.</td></tr><tr><td><code>place.sameAs[]</code></td><td>array</td><td>URLs for social or external location pages.</td></tr><tr><td><code>categoryLabels[]</code></td><td>array</td><td>Tags such as <code>skip-the-line</code>, <code>wheelchair-accessible</code>.</td></tr><tr><td><code>durationMinutesFrom</code></td><td>integer</td><td>Minimum or exact duration in minutes.</td></tr><tr><td><code>durationMinutesTo</code></td><td>integer | null</td><td>Maximum duration if flexible; null if fixed.</td></tr><tr><td><code>commentary[]</code></td><td>array</td><td>Commentary formats and languages.</td></tr><tr><td><code>commentary[].format</code></td><td>string (enum)</td><td>One of: <code>IN_PERSON</code>, <code>RECORDED_AUDIO</code>, <code>WRITTEN</code>, <code>OTHER</code>.</td></tr><tr><td><code>commentary[].language</code></td><td>string (IETF BCP 47 tag)</td><td>Language code (e.g., <code>en-GB</code>, <code>fr-FR</code>).</td></tr><tr><td><code>defaultCurrency</code></td><td>string (ISO 4217 alpha code)</td><td>Default currency of the product. Returned with <code>octo/pricing</code> capability.</td></tr><tr><td><code>availableCurrencies</code></td><td>array</td><td>List of supported currencies. Returned with <code>octo/pricing</code> capability.</td></tr><tr><td><code>pricingPer</code></td><td>array (enum: UNIT, BOOKING)</td><td>Defines whether pricing is per unit or per booking. Returned with <code>octo/pricing</code> capability.</td></tr></tbody></table>

#### Example: Product Object with `content` and `pricing` Capabilities

```json
{
  "id": "6b903d44-dc24-4ca4-ae71-6bde6c4f4854", // OCTO Core field
  "reference": "LEYE", // OCTO Core field
  "internalName": "London Eye Admission", // OCTO Core field
  "locale": "en-GB", // OCTO Core field
  "timeZone": "Europe/London", // OCTO Core field
  "allowFreesale": true, // OCTO Core field
  "availabilityRequired": true, // OCTO Core field
  "instantConfirmation": true, // OCTO Core field
  "instantDelivery": true, // OCTO Core field
  "availabilityType": "OPENING_HOURS", // OCTO Core field
  "deliveryMethods": ["TICKET", "VOUCHER"], // OCTO Core field
  "deliveryFormats": ["QRCODE", "PDF_URL", "PKPASS_URL"], // OCTO Core field
  "redemptionMethod": "DIGITAL", // OCTO Core field,

  "title": "The Official London Eye Entry Ticket", 
  "shortDescription": "Experience breathtaking 360-degree views of London from the iconic London Eye.", 
  "description": "Step into one of the 32 high-tech glass capsules and ascend 135 meters above the River Thames...", 

  "features": [
    { "shortDescription": "Air-conditioned glass capsules", "type": "INCLUSION" },
    { "shortDescription": "Free Wi-Fi at the boarding area", "type": "INCLUSION" },
    { "shortDescription": "Wheelchair accessible", "type": "ACCESSIBILITY_INFORMATION" },
    { "shortDescription": "Baby changing facilities available", "type": "ACCESSIBILITY_INFORMATION" },
    { "shortDescription": "No food or drink allowed inside capsules", "type": "PREBOOKING_INFORMATION" },
    { "shortDescription": "Binoculars available at the gift shop", "type": "ADDITIONAL_INFORMATION" }
  ],

  "faqs": [
    { "question": "How long does a ride on the London Eye take?", "answer": "A full rotation takes approximately 30 minutes." },
    { "question": "Can I bring my luggage?", "answer": "Large luggage items are not allowed." },
    { "question": "Are pets allowed?", "answer": "Only assistance dogs are permitted." },
    { "question": "Is there a fast-track option?", "answer": "Yes, fast-track tickets are available for an additional fee." }
  ],

  "media": [
    {
      "src": "https://www.londoneye.com/media/london-eye-day.jpg",
      "type": "image/jpeg",
      "rel": "COVER",
      "title": "London Eye Daytime View",
      "caption": "Panoramic view of the London Eye in daylight.",
      "copyright": "© London Eye"
    },
    {
      "src": "https://www.londoneye.com/media/london-eye-night.jpg",
      "type": "image/jpeg",
      "rel": "GALLERY",
      "title": "London Eye at Night",
      "caption": "The illuminated wheel at night.",
      "copyright": "© London Eye"
    },
    {
      "src": "https://www.youtube.com/watch?v=abc123",
      "type": "external/youtube",
      "rel": "GALLERY",
      "title": "Experience the London Eye",
      "caption": "Promotional video overview."
    },
    {
      "src": "https://www.londoneye.com/media/brochure.pdf",
      "type": "application/pdf",
      "rel": "GALLERY",
      "title": "Visitor Brochure",
      "caption": null
    }
  ],

  "locations": [
    {
      "title": "London Eye",
      "shortDescription": "Observation wheel on the South Bank of the River Thames.",
      "types": ["POINT_OF_INTEREST", "REDEMPTION", "ADMISSION_INCLUDED"],
      "place": {
        "latitude": 51.5033,
        "longitude": -0.1195,
        "postalAddress": {
          "streetAddress": "Riverside Building, County Hall",
          "addressLocality": "London",
          "addressRegion": null,
          "postalCode": "SE1 7PB",
          "addressCountry": "GB",
          "postOfficeBoxNumber": null
        },
        "identifiers": [
          { "googlePlaceId": "ChIJc2nSALkEdkgRkuoJJBfzkUI" }
        ],
        "sameAs": [
          "https://www.londoneye.com"
        ]
      }
    }
  ],

  "categoryLabels": ["skip-the-line", "wheelchair-accessible", "family-friendly"],

  "durationMinutesFrom": 30,
  "durationMinutesTo": null,

  "commentary": [
    { "format": "RECORDED_AUDIO", "language": "en-GB" },
    { "format": "WRITTEN", "language": "fr-FR" }
  ],

  "defaultCurrency": "GBP", // pricing capability field
  "availableCurrencies": ["GBP", "EUR", "USD"], // pricing capability field
  "pricingPer": ["UNIT"] // pricing capability field
}
```

### Option

The following fields are added to the OCTO Core Option object when the `content` capability is supported and requested.

> ⚠️ **Content Field Reuse Across Levels**
>
> Certain `content` fields such as `features`, `faqs`, `media`, `locations`, `categoryLabels`, and `commentary` are intentionally repeated across the Product and Option levels.
>
> Implementer supplier systems must return these fields only at the most appropriate level (product or option) depending on the relevance of the content.
>
> Reseller systems are expected to concatenate or display these fields according to their presentation logic, combining product-level and option-level data where suitable.

#### Enriched Fields (from `content` Capability)

| Field                         | Type (Format)                                                                                | Description                                                                                                                                                                                                 |
| ----------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title`                       | string                                                                                       | Public, customer-facing name of the option.                                                                                                                                                                 |
| `shortDescription`            | string \| null                                                                               | A brief summary of the option. May be null.                                                                                                                                                                 |
| `description`                 | string \| null                                                                               | A detailed description of the option, highlighting experience-specific details.                                                                                                                             |
| `features[]`                  | array (feature object)                                                                       | Customer-facing feature details specific to this option.                                                                                                                                                    |
| `features[].shortDescription` | string \| null                                                                               | Short summary of a feature (e.g., "Fast Track entry included").                                                                                                                                             |
| `features[].type`             | string (enum)                                                                                | One of: INCLUSION, EXCLUSION, HIGHLIGHT, PREBOOKING\_INFORMATION, PREARRIVAL\_INFORMATION, REDEMPTION\_INSTRUCTION, ACCESSIBILITY\_INFORMATION, ADDITIONAL\_INFORMATION, BOOKING\_TERM, CANCELLATION\_TERM. |
| `faqs[]`                      | array (faq object)                                                                           | Option-specific questions and answers to clarify expectations.                                                                                                                                              |
| `faqs[].question`             | string                                                                                       | The question text.                                                                                                                                                                                          |
| `faqs[].answer`               | string                                                                                       | Answer text providing clarity or support.                                                                                                                                                                   |
| `media[]`                     | array (media object)                                                                         | Images or videos specific to the option, if applicable.                                                                                                                                                     |
| `media[].src`                 | string (uri)                                                                                 | Stable, publicly accessible URL pointing to the media file.                                                                                                                                                 |
| `media[].type`                | string (enum: image/jpeg, image/png, video/mp4, video/avi, external/youtube, external/vimeo) | Format of the media.                                                                                                                                                                                        |
| `media[].rel`                 | string (enum: LOGO, COVER, GALLERY)                                                          | The relation of this media to the option (branding, cover, gallery).                                                                                                                                        |
| `media[].title`               | string \| null                                                                               | Optional title or identifier.                                                                                                                                                                               |
| `media[].caption`             | string \| null                                                                               | Caption shown alongside the media asset, if any.                                                                                                                                                            |
| `media[].copyright`           | string \| null                                                                               | Copyright notice or license info, if required.                                                                                                                                                              |

```json
...

  "id": "2214b32d-ffa1-426c-b4d2-947dca724e50",
  "reference": "LEYE-FT",
  "default": false,
  "internalName": "Fast Track",
  "availabilityLocalStartTimes": ["10:00", "11:00", "12:00"],
  "cancellationCutoff": "24 hours",
  "cancellationCutoffAmount": 24,
  "cancellationCutoffUnit": "hour",
  "requiredContactFields": ["firstName", "lastName"],
  "restrictions": {
    "minUnits": 1,
    "maxUnits": 20
  },
  "title": "Fast Track Admission to the London Eye",
  "shortDescription": "Skip the lines with fast track entry to the iconic observation wheel.",
  "description": "Enjoy priority access and spend less time queuing for your unforgettable London Eye experience.",
  "features": [
    {
      "shortDescription": "Priority boarding through Fast Track entrance",
      "type": "INCLUSION"
    },
    {
      "shortDescription": "Tickets non-transferable and non-refundable",
      "type": "CANCELLATION_TERM"
    }
  ],
  "faqs": [
    {
      "question": "Do I need to queue if I have Fast Track?",
      "answer": "Fast Track tickets allow you to bypass the standard line, but short waits may still occur."
    }
  ],
  "media": [
    {
      "src": "https://www.londoneye.com/media/fast-track-entry.jpg",
      "type": "image/jpeg",
      "rel": "GALLERY",
      "title": "Fast Track Entrance",
      "caption": "Guests entering through the Fast Track gate",
      "copyright": "© London Eye"
    }
  ]
}
```

### Unit&#x20;

The following fields are added to the OCTO Core **Unit** object when the `content` capability is supported and requested:&#x20;

#### Enriched Fields (from `content` Capability)

| Field                         | Type (Format)                                                                                                                      | Field Description                                                                |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `title`                       | string                                                                                                                             | Customer-facing name of the unit (e.g., "Adult", "Student").                     |
| `shortDescription`            | string \| null                                                                                                                     | Brief summary describing the unit's eligibility or characteristics.              |
| `features[]`                  | array (feature object)                                                                                                             | Features specific to this unit, such as inclusions, exclusions, or safety notes. |
| `features[].shortDescription` | string                                                                                                                             | A short, clear label describing the unit-specific feature.                       |
| `features[].type`             | string (enum: INCLUSION, EXCLUSION, HIGHLIGHT, MUST\_KNOW, SAFETY\_INFORMATION, REDEMPTION\_INSTRUCTIONS, ADDITIONAL\_INFORMATION) | Categorizes the feature for display and filtering.                               |

```json
...
  {
    "id": "unit_adult_001", // OCTO Core
    "reference": "ADULT-TICKET", // OCTO Core
    "internalName": "Adult Ticket", // OCTO Core
    "type": "ADULT", // OCTO Core
    "requiredContactFields": ["firstName", "lastName"], // OCTO Core
    "restrictions": {
      "minAge": 18,
      "maxAge": 64,
      "idRequired": false,
      "minQuantity": 1,
      "maxQuantity": 10,
      "paxCount": 1,
      "accompaniedBy": []
    },
    "title": "Adult", // content capability
    "shortDescription": "Standard ticket for adults aged 18 to 64." // content capability
  },
  {
    "id": "unit_child_001", // OCTO Core
    "reference": "CHILD-TICKET", // OCTO Core
    "internalName": "Child Ticket", // OCTO Core
    "type": "CHILD", // OCTO Core
    "requiredContactFields": ["firstName"], // OCTO Core
    "restrictions": {
      "minAge": 5,
      "maxAge": 12,
      "idRequired": false,
      "minQuantity": 1,
      "maxQuantity": 5,
      "paxCount": 1,
      "accompaniedBy": ["unit_adult_001"]
    },
    "title": "Child", // content capability
    "shortDescription": "Discounted ticket for children aged 5 to 12.", // content capability
    "features": [
      {
        "shortDescription": "Access to children's exhibits and audio guide",
        "type": "INCLUSION"
      },
      {
        "shortDescription": "Must be accompanied by an adult",
        "type": "MUST_KNOW"
      }
    ]
  }
...
```

### Availability

The following fields are added to the OCTO Core Availability object when the `content` capability is supported and requested.

| Field              | Type (Format)  | Field Description                                                              |
| ------------------ | -------------- | ------------------------------------------------------------------------------ |
| `title`            | string \| null | Public-facing name for the availability slot. Can be null when not applicable. |
| `shortDescription` | string \| null | A brief summary description of the availability window or conditions.          |

```json
[
  {
    "id": "2025-05-01T20:15:00+01:00", // OCTO Core
    "localDateTimeStart": "2025-05-01T20:15:00+01:00", // OCTO Core
    "localDateTimeEnd": "2025-05-01T20:30:00+01:00", // OCTO Core
    "allDay": false, // OCTO Core
    "available": true, // OCTO Core
    "status": "LIMITED", // OCTO Core
    "vacancies": 3, // OCTO Core
    "capacity": 20, // OCTO Core
    "maxUnits": 3, // OCTO Core
    "openingHours": [ // OCTO Core
      { "from": "09:00", "to": "23:00" }
    ],
    "title": "Sunset Ride", 
    "shortDescription": "Perfect for catching golden hour views from the London Eye." 
  },
  {
    "id": "2025-05-01T21:00:00+01:00", // OCTO Core
    "localDateTimeStart": "2025-05-01T21:00:00+01:00", // OCTO Core
    "localDateTimeEnd": "2025-05-01T21:15:00+01:00", // OCTO Core
    "allDay": false, // OCTO Core
    "available": true, // OCTO Core
    "status": "LIMITED", // OCTO Core
    "vacancies": 5, // OCTO Core
    "capacity": 20, // OCTO Core
    "maxUnits": 5, // OCTO Core
    "openingHours": [ // OCTO Core
      { "from": "09:00", "to": "23:00" }
    ],
    "title": null, 
    "shortDescription": null
  }
]
```


# Pickups \[NEW]

Adds structured pickup support, allowing predefined or customer-defined pickup locations, including pickup time windows.

### Business Context

Pickup service is commonly offered for tours and activities. It allows customers to be collected from one of the designated locations, or even from a customer-defined point within a supported pickup area, instead of the main meeting point.

This service may be included for free or offered at an additional cost.

The OCTO Pickups Capability allows:

* **Supplier systems** to indicate whether a product option supports pickup, whether pickup is required, and to provide structured pickup locations and areas.
* **Reseller systems** to obtain pickup data, display available choices to customers during the booking flow, pass the customer’s selected pickup location when creating, confirming, or updating a booking, and leverage a capability-specific endpoint to validate pickup availability based on a customer-defined point (e.g., latitude/longitude).

\
Identical functionality is also available for dropoffs through the [Dropoffs Capability,](/capabilities-optional/dropoffs-new) which mirrors pickups and provides structured or customer-defined end-of-tour return options.

### Capability Summary

To enable this capability when supported by a supplier system’s OCTO API, the reseller system must include `pickups` in the `Octo-Capabilities` header of its requests to the supplier system.

Adding `pickups` to your `Octo-Capabilities` header will alter OCTO Core endpoint behavior as follows:

<table data-full-width="true"><thead><tr><th width="254.8671875" data-type="content-ref">Endpoint</th><th>Pickups Capability Enhancements to OCTO Core</th></tr></thead><tbody><tr><td><a href="/pages/kSwPw2aS5dPcZH4tcgYo#get-supplier">/pages/kSwPw2aS5dPcZH4tcgYo#get-supplier</a></td><td><em>No additional functionality.</em></td></tr><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product-list">/pages/YYk287KcjoL5Y9f4gxHH#get-product-list</a></td><td><a href="#get-product-list-and-get-product-with-pickups">Response</a> payload includes, per product option, whether pickup is available, required for booking, and a list of supported locations/areas with their details. </td></tr><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product">/pages/YYk287KcjoL5Y9f4gxHH#get-product</a></td><td><a href="#get-product-list-and-get-product-with-pickups">Response</a> payload includes, per product option, whether pickup is available, required for booking, and a list of supported locations/areas with their details. </td></tr><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-calendar">/pages/YGhhlqLzirsUfsgugSS2#availability-calendar</a></td><td><a href="#post-availability-calendar-with-pickups">Request</a> can specify if the customer wants pickup and the chosen location. <a href="#post-availability-calendar-with-pickups">Response</a> includes, per calendar day, whether pickup is available, required, and the list of supported locations/areas with details. </td></tr><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-check">/pages/YGhhlqLzirsUfsgugSS2#availability-check</a></td><td><a href="#post-availability-check-with-pickups">Request</a> can specify if the customer wants pickup and the chosen location. <a href="#post-availability-check-with-pickups">Response</a> includes, per availability, whether pickup is available, required, the list of supported locations/areas with details, including pickup time window.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#create-booking">/pages/lnoP1fBP544WzZQH7CuB#create-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Request</a> can specify if the customer wants pickup, the chosen location, as well as any pickup notes for booking. <a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#extend-pending-booking-expiration">/pages/lnoP1fBP544WzZQH7CuB#extend-pending-booking-expiration</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#confirm-booking">/pages/lnoP1fBP544WzZQH7CuB#confirm-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#cancel-booking">/pages/lnoP1fBP544WzZQH7CuB#cancel-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#update-booking">/pages/lnoP1fBP544WzZQH7CuB#update-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Request</a> can specify if the customer wants pickup, the chosen location, as well as any pickup notes for booking. <a href="/pages/lnoP1fBP544WzZQH7CuB#update-booking">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking-list">/pages/lnoP1fBP544WzZQH7CuB#get-booking-list</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking">/pages/lnoP1fBP544WzZQH7CuB#get-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the pickup is requested for the booking, the selected pickup location with details, including the pickup time window. </td></tr></tbody></table>

\
**GET Booking Pickup Locations Endpoint**

Additionally, this capability provides a dedicated endpoint [GET Booking Pickup Locations](#get-booking-pickup-locations), which enables searching for pickup locations based on a customer-defined latitude and longitude, and validates whether pickup service is available at that exact location or recommends alternate pickup locations.&#x20;

### Additional OCTO Core Parameters & Schemas

#### [**GET Product List**](/octo-api-core/products#get-product-list) **AND** [**GET Product**](/octo-api-core/products#get-product) **(with `pickups`)**

**Request Body:**

```json
{
// ...rest of the OPTION object
  "pickupAvailable": true,
  "pickupRequired": false,
  "pickupLocations": [
    {
      "id": "e7c0f5a6-3b3a-4c8f-9a1d-2f4a7a9b1c23",
      "title": "Hotel Avenida Palace",
      "shortDescription": "Pickup available directly from Hotel Avenida Palace, located in the heart of Lisbon near Rossio Square. Guests should wait at the main lobby entrance.",
      "place": {
        "latitude": 38.7123,
        "longitude": -9.1334,
        "postalAddress": {
          "streetAddress": "Rua 1",
          "addressLocality": "Lisboa",
          "addressRegion": "Lisboa",
          "postalCode": "1000-000",
          "addressCountry": "PT",
          "postOfficeBoxNumber": null
        },
        "identifiers": [
          { "googlePlaceId": "ChIJd7zN_th2GQ0Rj..." },
          { "tripadvisorLocationId": "123456" }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=..."
        ]
      },
     },
    {
      "id": "9f1b0a77-6d52-4f0a-9b7c-3d1a2b5c6e88",
      "title": "Praça do Comércio",
      "shortDescription": "Pickup available from Lisbon’s iconic Praça do Comércio, a large public square facing the Tagus River. Meeting point is next to the central equestrian statue.",
      "place": {
        "latitude": 38.7079,
        "longitude": -9.1366,
        "postalAddress": {
          "streetAddress": "Praça do Comércio",
          "addressLocality": "Lisboa",
          "addressRegion": "Lisboa",
          "postalCode": "1100-148",
          "addressCountry": "PT",
          "postOfficeBoxNumber": null
        },
        "identifiers": [
          { "googlePlaceId": "ChIJW0..." }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=..."
        ]
      },
    }
  ],
  # Optional, just intended as a visual aid
  "pickupAreas": [
    {
      "title": "Downtown Zone",
      "shortDescription": "Pickup available across the downtown Lisbon area, covering hotels, landmarks, and popular meeting points such as Rossio, Baixa, and Cais do Sodré. Guests can be collected from designated stops within this zone.",
      "area": {
        "coordinates": [
          { "latitude": 38.7139, "longitude": -9.1437 },
          { "latitude": 38.7132, "longitude": -9.1379 },
          { "latitude": 38.7098, "longitude": -9.1375 },
          { "latitude": 38.7139, "longitude": -9.1437 } # end up where you start
        ],
        "identifiers": [
          { "googlePlaceId": "ChIJd7zN_th2GQ0Rj..." },
          { "tripadvisorLocationId": "123456" }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=...",
          "https://www.tripadvisor.com/Attraction_Review-..."
        ]
      },
    }
  ]
}
```

#### [**POST Availability Calendar**](/octo-api-core/availability#availability-calendar) **(with `pickups`)** &#x20;

**Request Body:**

```json
{
  // ... rest of the OCTO Core availability request body
  "pickupRequested": true,
  "pickupLocationId": "9f1a3c1e-6b28-4e2a-9c7e-32d5a3a1f45e"
}
```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pickupRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested pickup. </td></tr><tr><td><code>pickupLocationId</code></td><td>string</td><td>optional</td><td>The pickup location ID selected by the customer (must match one of <code>pickupLocations[].id)</code>.</td></tr></tbody></table>

**Response Body:**&#x20;

```json
{
  // ...rest of the CALENDAR DAY object
  "pickupAvailable": true,
  "pickupRequired": false
}
```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pickupAvailable</code></td><td>boolean</td><td>required</td><td>Indicates whether pickup service is offered for this date.</td></tr><tr><td><code>pickupRequired</code></td><td>boolean</td><td>required</td><td>Indicates whether selecting a pickup location is mandatory for booking.</td></tr></tbody></table>

#### [**POST Availability Check**](/octo-api-core/availability#post-availability) **(with `pickups`)** &#x20;

**Request Body:**

```json
{
// ... rest of the OCTO Core availability request body
  "pickupRequested": true,
  "pickupLocationId": null (optional, if pickupLocation is requested up-front)
}

```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pickupRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested pickup. </td></tr><tr><td><code>pickupLocationId</code></td><td>string</td><td>optional</td><td>The pickup location ID selected by the customer (must match one of <code>pickupLocations[].id)</code>.</td></tr></tbody></table>

**Response Body:**&#x20;

```json
{
// ...rest of the AVAILABILITY object
  // ...note addition of localDateTimeStart and localDateTimeEnd below 
  "pickupAvailable": true,
  "pickupRequired": false,
  "localPickupDateTimeStart": null, (only set if pickupLocationId is set)
  "localPickupDateTimeEnd": null, (only set if pickupLocationId is set)
}

```

<table data-full-width="true"><thead><tr><th>Field</th><th width="242.22265625">Type</th><th width="118.24609375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>localPickupDateTimeStart</code></td><td>string (<a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a>, i.e. YYYY-MM-DD)</td><td>optional</td><td>Start of the pickup time window in the local time zone of the pickup location. Indicates the earliest time the customer can be collected. </td></tr><tr><td><code>localPickupDateTimeEnd</code></td><td>string (<a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a>, i.e. YYYY-MM-DD)</td><td>optional</td><td>End of the pickup time window in the local time zone of the pickup location. Indicates the latest time the customer can be collected.</td></tr></tbody></table>

#### [**POST Create Booking**](/octo-api-core/bookings#create-booking) **/** [**PATCH Update Booking**](/octo-api-core/bookings#update-booking) **(with `pickups`)**&#x20;

**Request Body:**

```json
{
// ... rest of the OCTO Core CREATE BOOKING request body
  "pickupRequested": true,
  "pickupLocationId": "9f1a3c1e-6b28-4e2a-9c7e-32d5a3a1f45e"
  "pickupNotes": "Blue door next to the bakery"
}
```

<table data-full-width="true"><thead><tr><th>Field</th><th width="158.88671875">Type</th><th width="126.9609375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>pickupRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested pickup. </td></tr><tr><td><code>pickupLocationId</code></td><td>string</td><td>optional</td><td>The pickup location ID selected by the customer (must match one of pickupLocations[].id).</td></tr><tr><td><code>pickupNotes</code></td><td>string</td><td>optional</td><td>Free-text notes provided by the customer for pickup (e.g., Airbnb directions, gate code, nearby landmark, hotel room, etc).</td></tr></tbody></table>

**Response Body:**

```json
 // ...rest of the BOOKING object
  "pickupRequested": true,
  "pickupLocationId": "e7c0f5a6-3b3a-4c8f-9a1d-2f4a7a9b1c23",
  "pickupNotes": "Blue door next to the bakery",
  "localPickupDateTimeStart": "2025-12-01T05:40:00-08:00",
  "localPickupDateTimeEnd": "2025-12-01T05:55:00-08:00",
  "pickupLocation": {
    "id": "e7c0f5a6-3b3a-4c8f-9a1d-2f4a7a9b1c23",
    "title": "Hotel Avenida Palace",
    "shortDescription": "Pickup available directly from Hotel Avenida Palace, located in the heart of Lisbon near Rossio Square. Guests should wait at the main lobby entrance.",
    "place": {
      "latitude": 38.7123,
      "longitude": -9.1334,
      "postalAddress": {
        "streetAddress": "Rua 1",
        "addressLocality": "Lisboa",
        "addressRegion": "Lisboa",
        "postalCode": "1000-000",
        "addressCountry": "PT",
        "postOfficeBoxNumber": null
      },
      "identifiers": [
        { "googlePlaceId": "ChIJd7zN_th2GQ0Rj..." },
        { "tripadvisorLocationId": "123456" }
      ],
      "sameAs": [
        "https://maps.google.com/?cid=..."
      ]
    }
  }
}
```

### GET Booking Pickup Locations

`GET` {host}/bookings/{uuid}/pickupLocations?latitude={lat}\&longitude={lng}

**Query Parameters:**&#x20;

<table data-full-width="true"><thead><tr><th>Name</th><th>Type</th><th width="185.03515625">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>latitude</code></td><td>number</td><td>required</td><td>Customer-defined latitude (WGS84).</td></tr><tr><td><code>longitude</code></td><td>number</td><td>required</td><td>Customer-defined longitude (WGS84).</td></tr></tbody></table>

When latitude and longitude are not provided, the supplier system should return the full list of pickup locations.

When latitude and longitude *are* provided, the supplier system may choose to either:

* return the full list of pickup locations in any order,
* return the full list ordered in a way that reflects the lat/lon query,
* return only a subset of locations that best match the passenger’s position, or
* generate a new, virtual pickup location on the fly that corresponds to the provided coordinates and can be used for booking creation or updates.

Reseller systems must be prepared for scenarios where a pickup location ID returned during availability or booking does not appear in the static list retrieved via the general `pickupLocations` query for that product option.

**Response Body:**

```json
{
    "localPickupDateTimeStart": "2025-12-01T05:40:00-08:00",
    "localPickupDateTimeEnd": "2025-12-01T05:55:00-08:00",
    "pickupLocation": {
      "id": "9f1a3c1e-6b28-4e2a-9c7e-32d5a3a1f45e",
      "title": "Americania Hotel - 121 7th St",
      "shortDescription": "Meet next to the parking lot entrance.",
      "place": {
        "latitude": 37.7788005,
        "longitude": -122.4102065,
        "postalAddress": {
          "streetAddress": "121 7th Street",
          "addressLocality": "San Francisco",
          "addressRegion": "California",
          "postalCode": "94103",
          "addressCountry": "US"
        },
        "identifiers": [
          { "googlePlaceId": "ChIJOTbxc4OAhYARrK82JwxWZFY" }
        ],
        "sameAs": [
          "https://www.americaniahotel.com",
          "https://maps.google.com/?q=121+7th+St+San+Francisco"
        ]
      }
    }
  },
  {
    "localPickupDateTimeStart": "2025-12-01T06:10:00-08:00",
    "localPickupDateTimeEnd": "2025-12-01T06:25:00-08:00",
    "pickupLocation": {
      "id": "d41e236b-3927-48ad-9c68-66c3a4789a2f",
      "title": "Union Square - 333 Post St",
      "shortDescription": "Pickup near the central monument.",
      "place": {
        "latitude": 37.788056,
        "longitude": -122.4075,
        "postalAddress": {
          "streetAddress": "333 Post Street",
          "addressLocality": "San Francisco",
          "addressRegion": "California",
          "postalCode": "94108",
          "addressCountry": "US"
        }
      }
    },
  }
]
```


# Dropoffs \[NEW]

Adds structured dropoff support, allowing predefined or customer-defined end-of-tour return locations, including time-window details.

### Business Context

Dropoff service is commonly offered for tours and activities. It allows customers to be returned to one of the designated locations, or even to a customer-defined point within a supported dropoff area, instead of being left at the main ending point.

This service may be included for free or offered at an additional cost.

The OCTO Dropoffs Capability allows:

* **Supplier systems** to indicate whether a product option supports dropoff, whether dropoff is required, and to provide structured dropoff locations and areas.
* **Reseller systems** to obtain dropoff data, display available choices to customers during the booking flow, pass the customer’s selected dropoff location when creating, confirming, or updating a booking, and leverage a capability-specific endpoint to validate dropoff availability based on a customer-defined point (e.g., latitude/longitude).

Identical functionality is also available for pickups through the [Pickups](/capabilities-optional/pickups-new), which mirrors dropoffs and provides structured or customer-defined start-of-tour collection options.

### Capability Summary

To enable this capability when supported by a supplier system’s OCTO API, the reseller system must include `dropoffs` in the `Octo-Capabilities` header of its requests to the supplier system.

Adding `dropoffs` to your `Octo-Capabilities` header will alter OCTO Core endpoint behavior as follows:

<table data-full-width="true"><thead><tr><th width="254.8671875" data-type="content-ref">Endpoint</th><th>Dropoffs Capability Enhancements to OCTO Core</th></tr></thead><tbody><tr><td><a href="/pages/kSwPw2aS5dPcZH4tcgYo#get-supplier">/pages/kSwPw2aS5dPcZH4tcgYo#get-supplier</a></td><td><em>No additional functionality.</em></td></tr><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product-list">/pages/YYk287KcjoL5Y9f4gxHH#get-product-list</a></td><td><a href="#get-product-list-and-get-product-with-pickups">Response</a> payload includes, per product option, whether dropoff is available, required for booking, and a list of supported locations/areas with their details. </td></tr><tr><td><a href="/pages/YYk287KcjoL5Y9f4gxHH#get-product">/pages/YYk287KcjoL5Y9f4gxHH#get-product</a></td><td><a href="#get-product-list-and-get-product-with-pickups">Response</a> payload includes, per product option, whether dropoff is available, required for booking, and a list of supported locations/areas with their details. </td></tr><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-calendar">/pages/YGhhlqLzirsUfsgugSS2#availability-calendar</a></td><td><a href="#post-availability-calendar-with-pickups">Request</a> can specify if the customer wants dropoff and the chosen location. <a href="#post-availability-calendar-with-pickups">Response</a> includes, per calendar day, whether dropoff is available, required, and the list of supported locations/areas with details. </td></tr><tr><td><a href="/pages/YGhhlqLzirsUfsgugSS2#availability-check">/pages/YGhhlqLzirsUfsgugSS2#availability-check</a></td><td><a href="#post-availability-check-with-pickups">Request</a> can specify if the customer wants dropoff and the chosen location. <a href="#post-availability-check-with-pickups">Response</a> includes, per availability, whether dropoff is available, required, the list of supported locations/areas with details, including dropoff time window.</td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#create-booking">/pages/lnoP1fBP544WzZQH7CuB#create-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Request</a> can specify if the customer wants dropoff, the chosen location, as well as any dropoff notes for booking. <a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#extend-pending-booking-expiration">/pages/lnoP1fBP544WzZQH7CuB#extend-pending-booking-expiration</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#confirm-booking">/pages/lnoP1fBP544WzZQH7CuB#confirm-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#cancel-booking">/pages/lnoP1fBP544WzZQH7CuB#cancel-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#update-booking">/pages/lnoP1fBP544WzZQH7CuB#update-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Request</a> can specify if the customer wants dropoff, the chosen location, as well as any dropoff notes for booking. <a href="/pages/lnoP1fBP544WzZQH7CuB#update-booking">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking-list">/pages/lnoP1fBP544WzZQH7CuB#get-booking-list</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr><tr><td><a href="/pages/lnoP1fBP544WzZQH7CuB#get-booking">/pages/lnoP1fBP544WzZQH7CuB#get-booking</a></td><td><a href="#post-create-booking-patch-update-booking-with-pickups">Response</a> includes if the dropoff is requested for the booking, the selected dropoff location with details, including the dropoff time window. </td></tr></tbody></table>

\
**GET Booking Dropoff Locations Endpoint**

Additionally, this capability provides a dedicated endpoint [GET Booking Dropoffs Locations](#get-booking-pickup-locations), which enables searching for dropoff locations based on a customer-defined latitude and longitude, and validates whether dropoff service is available at that exact location or recommends alternate dropoff locations.&#x20;

### Additional OCTO Core Parameters & Schemas

#### [**GET Product List**](/octo-api-core/products#get-product-list) **AND** [**GET Product**](/octo-api-core/products#get-product) **(with** `dropoffs`**)**

**Request Body:**

```json
{
// ...rest of the OPTION object
  "dropoffAvailable": true,
  "dropoffRequired": false,
  "dropoffLocations": [
    {
      "id": "c1b2a3d4-5e6f-7081-92a3-b4c5d6e7f801",
      "title": "Sagrada Família",
      "shortDescription": "Dropoff available near the Basílica de la Sagrada Família in Barcelona. Guests are dropped at the coach parking area on Carrer de la Marina.",
      "place": {
        "latitude": 41.4036,
        "longitude": 2.1744,
        "postalAddress": {
          "streetAddress": "Carrer de la Marina, 253",
          "addressLocality": "Barcelona",
          "addressRegion": "Catalunya",
          "postalCode": "08013",
          "addressCountry": "ES",
          "postOfficeBoxNumber": null
        },
        "identifiers": [
          { "googlePlaceId": "ChIJp2VwQ0-hpBIRsY0R5pY2W7E" },
          { "tripadvisorLocationId": "876543" }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=11111111111111111111"
        ]
      }
    },
    {
      "id": "f9e8d7c6-b5a4-3210-98f7-e6d5c4b3a210",
      "title": "Plaça de Catalunya",
      "shortDescription": "Central Barcelona dropoff at Plaça de Catalunya. Guests meet at the bus bays on the south side of the square.",
      "place": {
        "latitude": 41.3870,
        "longitude": 2.1701,
        "postalAddress": {
          "streetAddress": "Plaça de Catalunya",
          "addressLocality": "Barcelona",
          "addressRegion": "Catalunya",
          "postalCode": "08002",
          "addressCountry": "ES",
          "postOfficeBoxNumber": null
        },
        "identifiers": [
          { "googlePlaceId": "ChIJD7fiBh9uQgwR7-8fITgxTmU" }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=22222222222222222222"
        ]
      }
    }
  ],
  // Optional, just intended as a visual aid
  "dropoffAreas": [
    {
      "title": "Central Barcelona Zone",
      "shortDescription": "Dropoff available across central Barcelona, including Eixample, the Gothic Quarter, and the waterfront around Port Vell. Guests are dropped at approved coach stops within this zone.",
      "area": {
        "coordinates": [
          { "latitude": 41.3945, "longitude": 2.1682 },
          { "latitude": 41.3891, "longitude": 2.1830 },
          { "latitude": 41.3758, "longitude": 2.1820 },
          { "latitude": 41.3769, "longitude": 2.1635 },
          { "latitude": 41.3945, "longitude": 2.1682 } // end up where you start
        ],
        "identifiers": [
          { "googlePlaceId": "ChIJ5TCOcRaipBIRX9G6FFzC7m8" },
          { "tripadvisorLocationId": "654321" }
        ],
        "sameAs": [
          "https://maps.google.com/?cid=33333333333333333333",
          "https://www.tripadvisor.com/Attraction_Review-central-barcelona"
        ]
      }
    }
  ]
}

```

#### [**POST Availability Calendar**](/octo-api-core/availability#availability-calendar) **(with** `dropoffs`**)** &#x20;

**Request Body:**

```json
{
  // ... rest of the OCTO Core availability request body
  "dropoffRequested": true,
  "dropoffLocationId": "9f1a3c1e-6b28-4e2a-9c7e-32d5a3a1f45e"
}
```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>dropoffRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested dropoff. </td></tr><tr><td><code>dropoffLocationId</code></td><td>string</td><td>optional</td><td>The dropoff location ID selected by the customer (must match one of <code>dropoffLocations[].id</code>).</td></tr></tbody></table>

**Response Body:**&#x20;

```json
{
  // ...rest of the CALENDAR DAY object
  "dropoffAvailable": true,
  "dropoffRequired": false
}
```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>dropoffAvailable</code></td><td>boolean</td><td>required</td><td>Indicates whether dropoff service is offered for this date.</td></tr><tr><td><code>dropoffRequired</code></td><td>boolean</td><td>required</td><td>Indicates whether selecting a dropoff location is mandatory for booking.</td></tr></tbody></table>

#### [**POST Availability Check**](/octo-api-core/availability#post-availability) **(with** `dropoffs`**)** &#x20;

**Request Body:**

```json
{
// ... rest of the OCTO Core availability request body
  "dropoffRequested": true,
  "dropoffLocationId": null (optional, if dropoffLocation is requested up-front)
}

```

<table data-full-width="true"><thead><tr><th width="184.6015625">Field</th><th width="125.7734375">Type</th><th width="141.08203125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>dropoffRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested dropoff. </td></tr><tr><td><code>dropoffLocationId</code></td><td>string</td><td>optional</td><td>The dropoff location ID selected by the customer (must match one of <code>dropoffLocations[].id</code>).</td></tr></tbody></table>

**Response Body:**&#x20;

```json
{
// ...rest of the AVAILABILITY object
  // ...note addition of localDateTimeStart and localDateTimeEnd below 
  "dropoffAvailable": true,
  "dropoffRequired": false,
  "localDropoffDateTimeStart": null, (only set if dropoffLocationId is set)
  "localDropoffDateTimeEnd": null, (only set if dropoffLocationId is set)
}

```

<table data-full-width="true"><thead><tr><th>Field</th><th width="242.22265625">Type</th><th width="118.24609375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>localDropoffDateTimeStart</code></td><td>string (<a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a>, i.e. YYYY-MM-DD)</td><td>optional</td><td>Start of the dropoff time window in the local time zone of the dropoff location. Indicates the earliest time the customer can be collected. </td></tr><tr><td><code>localDropoffDateTimeEnd</code></td><td>string (<a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a>, i.e. YYYY-MM-DD)</td><td>optional</td><td>End of the dropoff time window in the local time zone of the dropoff location. Indicates the latest time the customer can be collected.</td></tr></tbody></table>

#### [**POST Create Booking**](/octo-api-core/bookings#create-booking) **/** [**PATCH Update Booking**](/octo-api-core/bookings#update-booking) **(with** `dropoffs`**)**&#x20;

**Request Body:**

```json
{
// ... rest of the OCTO Core CREATE BOOKING request body
  "dropoffRequested": true,
  "dropoffLocationId": "9f1a3c1e-6b28-4e2a-9c7e-32d5a3a1f45e"
  "dropoffNotes": "Blue door next to the bakery"
}
```

<table data-full-width="true"><thead><tr><th>Field</th><th width="158.88671875">Type</th><th width="126.9609375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>dropoffRequested</code></td><td>boolean</td><td>optional</td><td>Whether the customer requested dropoff. </td></tr><tr><td><code>dropoffLocationId</code></td><td>string</td><td>optional</td><td>The dropoff location ID selected by the customer (must match one of dropoffLocations[].id).</td></tr><tr><td><code>dropoffpNotes</code></td><td>string</td><td>optional</td><td>Free-text notes provided by the customer for dropoff (e.g., Airbnb directions, gate code, nearby landmark, hotel room, etc).</td></tr></tbody></table>

**Response Body:**

```json
// ...rest of the BOOKING object
{
  "dropoffRequested": true,
  "dropoffLocationId": "a3f9c2d1-7e45-4b89-91f2-5d6e7c8b9a10",
  "dropoffNotes": "Main entrance under the glass canopy",
  "localDropoffDateTimeStart": "2025-06-15T18:10:00+02:00",
  "localDropoffDateTimeEnd": "2025-06-15T18:25:00+02:00",
  "dropoffLocation": {
    "id": "a3f9c2d1-7e45-4b89-91f2-5d6e7c8b9a10",
    "title": "Hotel Colosseo View",
    "shortDescription": "Dropoff available at Hotel Colosseo View, a short walk from the Colosseum. Guests should wait at the coach bay in front of the main entrance on Via dei Fori Imperiali.",
    "place": {
      "latitude": 41.8910,
      "longitude": 12.4923,
      "postalAddress": {
        "streetAddress": "Via dei Fori Imperiali 12",
        "addressLocality": "Roma",
        "addressRegion": "Lazio",
        "postalCode": "00184",
        "addressCountry": "IT",
        "postOfficeBoxNumber": null
      },
      "identifiers": [
        { "googlePlaceId": "ChIJ0X31pIKJPxMRXkS7Gz5Q2f8" },
        { "tripadvisorLocationId": "789012" }
      ],
      "sameAs": [
        "https://maps.google.com/?cid=44444444444444444444"
      ]
    }
  }
}
```

### GET Booking Dropoff Locations

`GET` {host}/bookings/{uuid}/dropoffLocations?latitude={lat}\&longitude={lng}

**Query Parameters:**&#x20;

<table data-full-width="true"><thead><tr><th>Name</th><th>Type</th><th width="185.03515625">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>latitude</code></td><td>number</td><td>required</td><td>Customer-defined latitude (WGS84).</td></tr><tr><td><code>longitude</code></td><td>number</td><td>required</td><td>Customer-defined longitude (WGS84).</td></tr></tbody></table>

When latitude and longitude are not provided, the supplier system should return the full list of dropoff locations.

When latitude and longitude *are* provided, the supplier system may choose to either:

* return the full list of dropoff locations in any order,
* return the full list ordered in a way that reflects the lat/lon query,
* return only a subset of locations that best match the passenger’s position, or
* generate a new, virtual dropoff location on the fly that corresponds to the provided coordinates and can be used for booking creation or updates.

Reseller systems must be prepared for scenarios where a dropoff location ID returned during availability or booking does not appear in the static list retrieved via the general `dropoffLocations` query for that product option.

**Response Body:**

```json
{
  "localDropoffDateTimeStart": "2025-09-14T14:20:00+01:00",
  "localDropoffDateTimeEnd": "2025-09-14T14:35:00+01:00",
  "dropoffLocation": {
    "id": "b72c4e81-9d3f-4a3c-8a6f-0e92c4b1d993",
    "title": "Paddington Station – Praed St Entrance",
    "shortDescription": "Dropoff at the main Praed Street entrance, next to the taxi rank.",
    "place": {
      "latitude": 51.5167,
      "longitude": -0.1761,
      "postalAddress": {
        "streetAddress": "Praed Street",
        "addressLocality": "London",
        "addressRegion": "England",
        "postalCode": "W2 1HB",
        "addressCountry": "UK"
      },
      "identifiers": [
        { "googlePlaceId": "ChIJ5wCQT3YPdkgR0b6l5LjxaQ8" }
      ],
      "sameAs": [
        "https://maps.google.com/?q=Paddington+Station+Praed+Street"
      ]
    }
  }
},
{
  "localDropoffDateTimeStart": "2025-09-14T14:50:00+01:00",
  "localDropoffDateTimeEnd": "2025-09-14T15:05:00+01:00",
  "dropoffLocation": {
    "id": "e19a873b-04c3-48cc-9cb0-78bbcf6c1f44",
    "title": "London Eye – Riverside Walk",
    "shortDescription": "Dropoff near the entrance queue area on Riverside Walk.",
    "place": {
      "latitude": 51.5033,
      "longitude": -0.1195,
      "postalAddress": {
        "streetAddress": "Riverside Walk",
        "addressLocality": "London",
        "addressRegion": "England",
        "postalCode": "SE1 7PB",
        "addressCountry": "UK"
      }
    }
  }
}
```


# Promotions \[IN DEV]

Adds promotional pricing to availability and bookings, exposing promotional offers and their details.

{% hint style="danger" %}
This capability is currently being developed by the working group and has not yet undergone Specification Committee Review, Member Review, and Public Review phases before ratification.

OCTO members interested in joining the working group can find details in the **#promotions-capability-development** channel in the OCTO Slack workspace.

This page provides draft materials and ongoing updates from the working group, all of which remain subject to change throughout the review process.
{% endhint %}

### Business Context

Promotional (or discounted) pricing is a fundamental part of how tour and activity suppliers and resellers attract customers, drive conversions, and run marketing campaigns.

This extends the [Pricing Capability](/capabilities-optional/pricing) by allowing supplier systems to expose structured promotional offers to reseller systems during availability and pricing requests, and throughout the entire booking flow.&#x20;

This capability only covers price-based discounts (i.e., reductions to retail or net prices). More complex constructs, such as product bundling, will be defined under a separate capability.

The Promotions Capability requires the use of [Pricing Capability](/capabilities-optional/pricing). The additional fields provided through this capability describe promotional details associated with pricing, clarifying which discounts are available and/or have been applied.&#x20;

This supports both open promotions and gated offers such as member-only pricing, loyalty discounts, and promo codes.

### Working Draft:

<https://docs.google.com/document/d/18n_FmBM3T-BRwdRKW1D3d6dRqNuJFOeP6Z1q4ebYb90/edit?usp=sharing>


# Slack Workspace

### OCTO Slack Community

The OCTO Slack workspace is where the OCTO community collaborates, asks questions, and helps each other build better OCTO implementations. More than 300 members use it daily to share knowledge, discuss challenges, and stay aligned on the latest developments in the standard.

#### What it’s for

OCTO Slack is the best place to get support directly from fellow members—suppliers, resellers, developers, integrators, and working group contributors who are implementing OCTO in real production environments.\
\
You’ll find active channels covering implementation questions, capability discussions, working groups, announcements, and broader ecosystem topics.

#### Who can access it

The Slack workspace is available **exclusively to OCTO members**.

If you’re already a member and don’t have access yet, contact <help@octo.travel> and we’ll get you added.\
\
To become a member, visit <https://www.octo.travel/join>

#### How to access

If you’re an OCTO member, you can access the workspace at:\
<https://octo-travel.slack.com>


# Self-Certification Tool

The OCTO Self-Certification Tool is the new way for suppliers and resellers to validate their OCTO API implementation against the open standard. It runs automated tests against your system, confirms whether you meet the OCTO Core requirements, and generates a public certificate with an official “OCTO Certified” badge. The tool replaces the older OCTO Validator and introduces account-based management, recurring test runs, and a public directory of certified systems.

The tool is available at [**https://certify.octo.travel**](https://certify.octo.travel) and requires an OCTO login. Members and non-members can both use it (an option of a free account is available).

#### What it Does

The tool checks technical compliance with the OCTO API specification. It focuses on the fundamentals - schema validation, required fields, request structure, headers, paths, and expected error handling. It is not a functional or performance test: the goal is to verify that your implementation behaves consistently with the standard.

Depending on your system type, the tool works in one of two ways:

* **Supplier systems:**\
  You provide your OCTO API Host URL and Bearer Token. The tool sends real OCTO test calls to your system and validates the responses.
* **Reseller systems:**\
  You receive a mock OCTO endpoint and a unique token. Your system calls the mock endpoint, and the tool validates each request in real time.

When you reach a 100% pass rate, your certificate becomes **Valid** and appears in the public OCTO certification directory. You also receive an embeddable badge linking to your certificate page.

#### How to use it

1. **Log in**\
   Use your OCTO account. If you’re a member who logged in before, use “Forgot Password” to set credentials. Alternatively, create a new account.&#x20;
2. **Create a certificate**\
   Choose your system type - Supplier, Reseller - and enter the required details.&#x20;
3. **Run the tests**\
   For suppliers, the tool triggers calls to your OCTO API. For resellers, your system needs to call the issued mock endpoint.
4. **Review results**\
   Each test run shows its success/fail/warning status and any issues that need to be fixed.\
   A certificate becomes **Valid** when you have a full 100% successful run. Certificates remain valid for 12 months.
5. **Share your certification**\
   Every certificate has a public page and a badge - this is your official OCTO certificate to share with fellow industry implementers as proof of OCTO compliance. You can also embed the OCTO Certified badge on your website or partner materials.

#### Why it matters

Self-certification increases trust across the ecosystem. Implementers can prove their OCTO compliance, partners can verify readiness before an integration, and OCTO can maintain a transparent, up-to-date directory of systems following the standard. It lowers friction across the entire supply chain and helps everyone ship integrations more confidently.

If you need help or want to contribute feedback during the beta period, contact <help@octo.travel>.


# Postman Collections

OCTO API Postman Collection can be downloaded here:\
<https://github.com/octotravel/openapi/raw/main/OCTO%20API%20Specification.postman_collection.json>


# Known Implementations

See who already uses OCTO

The document below provides a summary of OCTO implementations by members as well as known implementations by non-members (as reported by members or known from public sources). The information provided is gathered by member volunteers, OCTO Standards NP Inc. is not responsible for the quality of the information in this document.\
\
<https://docs.google.com/spreadsheets/d/1UIgUYAPO3SUsdoGmg5xIGHIBHWHOx7XfQCOzOTML6ms/edit?usp=sharing>

**See a mistake or would like to contribute?** Get in touch with OCTO Implementation Support Team via [#implementation-support](https://octo-travel.slack.com/archives/C0573FGQG0J) on Slack (Members Only) or email <help@octo.travel>.

{% embed url="<https://docs.google.com/spreadsheets/d/1UIgUYAPO3SUsdoGmg5xIGHIBHWHOx7XfQCOzOTML6ms/edit?usp=sharing>" %}


