Skip to content

Dynamic Upsell widget

The Dynamic Upsell widget allows you to offer ancillaries within your existing customer journey with a UI built and continually optimized by Gordian. These are some of the benefits of the widget:

  • You can add ancillaries onto any flight booking from any ticketing channel.
  • You can select which products to display at different points.
  • You can save time and valuable resources by trusting the interactive UI development to Gordian's experts.

The following image is an example of the Gordian widget. The widget is highly customizable. For details on how to customize the appearance of the widget to your liking, contact your account manager.

Dynamic Upsell widget sample

Placing the widget

The Dynamic Upsell widget fits within the customer journey as part of the “Building a trip” step. This means that you must implement the rest of the steps by using the Gordian API directly. See the flights tutorial for more information on using the API.

The following table is a summary of which steps of the customer journey need which implementation:

Step Implementation Notes
1. Search for flights API You can create a new trip from scratch or from a record locator.
2. Add flight to the basket API You can skip this step if you used a record locator.
3. Add ancillaries API or Widget You can select which products to display.
4. Verify basket items API or Widget You must keep track of every item from the widget in your local basket.
5. Fulfill a trip API

Widget implementation

After searching for flights and selecting at least one through the API, you can use the Dynamic Upsell widget to display ancillaries to your customers. To add the widget to your website funnel, add the following tag to the page:

<script src=""></script>

Gordian provides the widget through a 5kb SDK. The file is available through a CDN to allow fast loading of your page.

This guide covers the following implementation steps:

  1. Initialize the SDK
  2. Display ancillaries
  3. Integrate the basket

Throughout this guide, you can refer to the sandbox example to see the completed backend and frontend integration.


If you fork the sandbox example, add the API_KEY environment variable in your code sandbox environment. For more information on secrets, see the Code Sandbox Secrets documentation.

Initialize the SDK

To initialize the Gordian SDK, call the init function. This function requires:

  • The trip_id and trip_access_token you received from creating a trip.
  • A callback function into onBasketChange. This function helps you keep track of the products the customer has selected and the state of those products. See integrate the basket for more information.

The following code is a sample of the init function:

  tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
  tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
  onBasketChange: onBasketChange

Additional Parameters

In addition to the required parameters mentioned above, there are additional optional parameters that can be passed into the init function.


If you've already performed a search for the trip, you can use that specific search by passing in the search_id into the searchIds parameter. Without this parameter, a new search will be used.

This is helpful if you've applied any parameters when creating the search that you don't want to include on the client side.

The following is an example for using a pre-existing baggage and seat search:

  tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
  tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
  onBasketChange: onBasketChange,
  searchIds: {
    bag: '02748ba9-5a86-4321-bfe8-aaedbe172075',
    seat: '02748ba9-5a86-4321-bfe8-aaedbe172075',


You may want to attach callbacks to the SDK to be notified of changes to the widgets state. We provide access to the following optional callbacks: - onSeatLoad - onSeatModalClosed - onBagLoad - onBagModalClosed - onSeatFail - onBagFail

These callbacks may be attached in the Gordian.init function, for example,

  tripId: "c1859ade-6ceb-4a7f-9a08-bf5ff33f1129",
  tripAccessToken: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0cl9pZCI6ImMxODU5YWRlLTZjZWItNGE3Zi05YTA4LWJmNWZmMzNmMTEyOSIsImluX2lkIjoiZjM5ZTRkZGQtNGM0Zi00ZTczLWJkOGUtNzBlNjRlZjBhNDdjIiwic2FuZGJveCI6ZmFsc2V9.F3hAHVoHut1Qkpm-wUUkK6b-_DHGMLpJ6CWf9A5ifuLBilwfK52aDVeW8axzwCA2dddKV8oq_Vdo90Z6sv-l_XfVoHsRkSBkSuZsLhj9TZ6WcYElstKr5NJxxUWG6muax_i4ceMTLbWIItr1QUtsSqVGHqrGj46Xvw5hebip-wCnhAjE3-N3rqzM_skDHnJEQwX9OiexrXFXtvwPyhp5UMaDCeuH2j9pw1VnURQVYsv8hBxdyCOWfdiHcwgFzKkSuja2PyYXTk9R1LgJm0KFICEbRer0L31lDYfuxo5_3OK8mnUD0P40xr4g9xoeiQkWumlDJ4fz6crIqlvv1hfj0g",
  onBasketChange: onBasketChange, 
  eventCallbacks: {
      onSeatLoad: () => { console.log("Foo") },
      onSeatFail: () => { console.log("Bar") },


To change the language shown in the widget, change the language parameter in the create trip call.

Display Ancillaries

Once the SDK is initialized, you can display products to the customers. To display products, use the showUpsell function. The following is a sample code snippet:

  container: document.getElementById("upsell-container"),
  display: "card", // card | embedded | modal
  allowProducts: ["seats"], //"bags", "fare_family_upsell", "priority_boading"...
  excludeProducts: [""],
  theme: {
    textPrimary: '#002244',

The showUpsell function has the following inputs:

  • container: The element ID of the container you want to display the widget within.
  • display: (Available for seats) This controls how the product is presented to the customer.
  • allowProducts: The product type you want to display. For example, use allowProducts:["seats"] to display only seats.
  • excludeProducts: The product type you do not want to display. For example, use excludeProducts:["seats"] to display all products but seats.
  • theme: Parts of the seatmap and baggage selection visualization can be configured through the theme parameter. More information on theming can be found here.

For more information about display types, see the product specific guides.

Using multiple display types

Each instance of the Dynamic Upsell widget can only have a single display type. To display two product types in two different displays, add two instances of the Dynamic Upsell widget on your page with different allowed and excluded products.

Integrate the basket

Now that your customers are able to view ancillaries, you need to keep track of the products the customer wants to purchase.


Any product that the customer selects must be added to your checkout basket. It is critical that you actively track which products are added or removed from the widget.

Whenever the basket has anything added or removed, or if an item in the basket expires, the widget calls the onBasketChange function. The following is a sample of this function:

const onBasketChange = ({ basket }) => {
  var basketBody = document.getElementById("basket-body");
  basketBody.innerHTML = "";
  for (var key in basket) {
    const product = basket[key];
    var row = document.createElement("tr");
    var productCell = document.createElement("td");
    var priceCell = document.createElement("td");
    productCell.innerText = product.display_name;
    priceCell.innerText = window.Gordian.formatPrice(,,
    var message = document.createElement("message");

    product.validity = product.validity;
    if (product.validity.state === "valid") {
    } else if (product.validity.state === "checking") {
      message.innerText = "Checking if this product is still available...";
    } else if (product.validity.state === "price_changed") {
      message.innerText = "The price for this product has changed!";
    } else if (product.validity.state === "unavailable") {
      message.innerText = "Sorry, this product is no longer available";

The single parameter to onBasketChange is a JSON representation of the whole customer trip. When you receive the JSON object, update your local basket with this new set by replacing all the products previously selected.

The basket includes a lot of granularity on each product. At a minimum, include the display_name and the price of each product in your basket for the customer's reference. The function Gordian.formatPrice helps you render the price for your user.

Products can become invalid because of changed availability or because of new passenger information, such as the age of the passenger or loyalty program information. When the validity changes, the onBasketChanged function retrieves different values for the validity field. You must check the new value to decide if there are actions needed. The possible values are the following:

Validity value Description Action
valid This product is still valid. N/A
checking Checking is in progress. Show a spinner to the user.
price_changed The price changed. Show the price change to the user and allow them to accept or reject it.
unavailable The product is not available. Inform the user, remove the product from the basket and call the showUpsell function to select an alternative.