# 8.HX PLATFORM

## Preview

Integrating something that you have not seen in action yet can be hard.\
To make the integration more tangible, it is helpful to see the widgets in your own shop in advance.\
You can simulate and preview all widgets with our demo mode. See section [**SANDBOX**](/sandbox/demo-integration) for detailed instructions.

## Integration

Integration into a shop is very simple and is done via our **8.SDK Web** (JavaScript SDK). Product data can be transmitted via API or file in push or pull mode.

{% hint style="info" %}
For **step-by-step instructions** see [**SHOP INTEGRATION**](https://docs.8select.io/integration)**.**
{% endhint %}

{% hint style="success" %}
Our customers have already integrated 8.HX into **SAP Commerce**, **novomind iShop** and individual shop systems via a manual integration within hours.
{% endhint %}

### Product Export

A prerequisite for the playout of content is the delivery of product data from your online shop.\
Our widgets will show product data such as prices. Products can be placed directly in the shopping cart from within our widgets. It is therefor necessary that updates for prices and stocks can be retrieved as fast as possible.


# 8.SDK Web (JavaScript SDK)

8.SDK Web

In order to display content in the shop, the **8.SDK Web** (JavaScript SDK) must be integrated. The variable `apiId` must be filled with the API ID provided by us.

```javascript
<script type="text/javascript">
    (function(d, s, w) {
      var apiId = '<API-ID>';
      w.eightlytics || function (w) {
          w.eightlytics = function () {
            window.eightlytics.queue = window.eightlytics.queue || [];
            window.eightlytics.queue.push(arguments);
          };
      }(w);
      var script = d.createElement(s);
      script.src   = 'https://wgt.8select.io/' + apiId + '/loader.js';
      var entry = d.getElementsByTagName(s)[0];
      entry.parentNode.insertBefore(script, entry);
    })(document, 'script', window);
</script>
```

{% hint style="warning" %}
The script should be placed as far up as possible in the DOM, preferably as first in the `<head>` tag to ensure it is executed as early as possible. Since the script is inserted dynamically, it is `async` by default and the loading of the page is not blocked. More about this [here](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement#dynamically_importing_scripts).

It is necessary to execute the script before you do the checkout tracking, otherwise tracking will not work.
{% endhint %}

{% hint style="info" %}
The script is required on all pages where 8SELECT widgets are used.\
For transaction tracking, it must also be included on the page **after checkout** where the tracking can happen.
{% endhint %}


# Widgets

UI elements for web

Content is displayed in the shop with the help of various widgets. The widgets are simple HTML elements and are filled with content by our 8.SDK Web.\
The widgets are configured via `data` attributes. There are static and dynamic attributes.

{% hint style="info" %}
The 8SELECT integration team will adjust the widget styling 🎨 to your shop design. ❤️&#x20;
{% endhint %}

### Example 8.SET

The widget is used to display cross-selling content in the form of product sets for a product.

```markup
<div data-8select-widget-id="8.SET" data-touchpoint="product" data-sku="42"></div>
```

{% hint style="info" %}
You can find a detailed description in the [**WIDGETS**](https://docs.8select.io/widgets/) section.
{% endhint %}

### Static Attributes

The value of these attributes does not change, for example`data-8select-widget-id` or `data-touchpoint`.

### Dynamic Attributes

The value of these attributes can change, for example `data-sku`. Depending on the product, a different value will be used here.

### Adding widget elements after 8.SDK was added

If you add 8SELECT widgets asynchronous the 8.SDK has to be informed about new widgets via its API.

```javascript
// failsafe if the 8.SDK is not yet injected
if (typeof _8select === "undefined") {
  return
}
// scan DOM for 8SELECT widgets and fill with content
_8select.initCSE();
```


# Add more elements, like custom headline

You can add any extra elements on top or below the widget.\
\
For example if you want to add your own custom headline markup.&#x20;

You just wrap the widget inside another `div` element and apply the css-class `-eightselect-widget-container`.

```html
<div class="-eightselect-widget-container">
    <div class="some-complicated markup">
        <h2>Complementary products you may like</h2>
        <div class="some-horizontal-spacer"></div>
    </div>
    <div data-8select-widget-id="8.SET" data-touchpoint="product" data-sku="42"></div>
</div>
```

In case there is no content or something prevents the SDK to show the widget, the whole recommendation area is hidden.


# Hide recommendation area in case there is no content

See [Add more elements, like custom headline](/integration/widgets/add-more-elements-like-custom-headline)


# Adding widgets after 8.SDK was loaded

If you add 8SELECT widgets asynchronous the 8.SDK has to be informed about new widgets via its API.

```javascript
// failsafe if the 8.SDK is not yet injected
if (typeof _8select === "undefined") {
  return
}
// scan DOM for new 8SELECT widgets and fill with content
_8select.initCSE();
```


# Internationalization

All our widgets have some static text elements. Those elements are fully configurable like the rest of the layout.

We can dynamically change the language of those elements based on the language the shop is currently using.&#x20;

As of now there is no way to configure the language the SDK should use, but it is on our roadmap.

For now our team will add custom code to your tenant's configuration to match the widget language to the shop language. Just reach out to your account manager!

{% hint style="info" %}
The 8SELECT integration team will adjust the widget texts for all languages your shop supports. ❤️&#x20;
{% endhint %}


# Shopping Cart

Allow to add products to your cart from within 8SELECT widgets

Many widgets allow a product to be added directly to the shopping cart.

Widgets that allow to add products to the cart will call the function `_eightselect_shop_plugin.addToCart()`. This function can then directly contain the code to add something to the shopping cart or call another function that is already implemented in the shop frontend.

{% hint style="warning" %}
To identify a product variant that should be added to the cart we currently only have the **sku** available. For **sku**, the same value as in the [product export](/product-export/base-data/details#sku-sku) is used.
{% endhint %}

```javascript
// 8.SDK Web configuration for shop's cart API
<script type="text/javascript">
  window._eightselect_shop_plugin = window._eightselect_shop_plugin || {};
  window._eightselect_shop_plugin.addToCart = function (sku, quantity, Promise) {
    // the function has to return a promise
    // you can use the injected Promise or use your own polyfill
    
    // add your cart logic here
    // this is just an example - shopApi is something you actually have to implement
    return new Promise(function(resolve, reject) {
      var response = shopApi.add2cart(sku);
      if (response === "OK") {        
        return resolve("done");
      }

      return reject(new Error("add2cart failed"));
    }
  }
</script>

// 8.SDK Web
<script type="text/javascript">
    ...
</script>
```

{% hint style="info" %}
The script is needed on all pages where widgets are used.
{% endhint %}

{% hint style="success" %}
A `Promise` object is injected into the function. A polyfill is provided for older browsers. This means that this does not have to be covered by the shop.
{% endhint %}

[<br>](https://app.gitbook.com/@8select/s/docs/~/drafts/-MRQJtvn2WI5oMAsvGIj/integration/warenkorb)


# Checkout Tracking

Transfer order data for analytics and performance based pricing

The commission is based on performance, i.e. only products purchased via our widgets are eligible for commission. To determine these products, the purchases in the shop are compared with the interactions in our widgets. For this purpose, all shop transactions must be transmitted. The transmission takes place without personal data and only contains the individual order items.

{% hint style="info" %}
To simplify the commission process it is based on **gross prices**, i.e. you have to transfer the **gross price** of a product.
{% endhint %}

### Code

{% hint style="warning" %}
price is the unit price, not the total price of that orderline - i.e if the user bought 3 items for 10 € each, the price would be 1000 (10€ in smallest currency factor)
{% endhint %}

```javascript
// 8.SDK Web
<script type="text/javascript">
    ...
</script>


// 8.LYTICS tracking for commission based pricing
<script type="text/javascript">
  window.eightlytics(
    'purchase',
      {
        customerid: 'anonymous', // string
        orderid: '1234', // string - unique
        products: [
          {
            sku: '12345', // string
            amount: 3, // integer
            price: 1199 // integer - gross price of 1 item in cent ! attention, this is not the total price
          },
          {
            sku: '456', // string
            amount: 1, // integer
            price: 19995 // integer
          }
        ]
    }  
  );
</script>
```

### How it will look in chrome's network tab

There should be a POST request going out to `https://eltx.8select.io/events`

![](/files/HMTkZJKWECKM5ZG5Yaly)

### Hints to consider

{% hint style="warning" %}
For **`price`**, the price in cents per item must be transferred as an **`integer`**.\
For an item that costs 199.95 €, the value `19995` must be transferred.&#x20;
{% endhint %}

{% hint style="warning" %}
For **sku**, the same value as in the [product export](/product-export/base-data/details#sku-sku) must be used. The value must be transmitted as a **string**.
{% endhint %}

{% hint style="info" %}
The **customerid** will be used to optimize content in the future. For the sake of simplicity and GDPR concerns we are removing it for now as a requirement. In the meantime just transfer `anonymous` as value.
{% endhint %}

{% hint style="danger" %}
The **orderid has to be unique** because we use it to deduplicate orders.

The **orderid** is used to compare our analytics data with the shop's analytics data on request. For example, for spot checks. If this is desired, the orderid must not be transmitted anonymously.
{% endhint %}


# Product Export

In order to be able to display the products in the widgets in a meaningful way, a basic set of data must be transmitted.

In order to access the [Fashion Content Pool](/product-export/fashion-content-pool), we need certain product properties so that our system can normalise the data and enrich the products with further properties.

{% hint style="info" %}
In the section [**PRODUCT EXPORT**](/product-export/data-transfer) the requirements and possibilities of data transfer are described in detail.
{% endhint %}

{% content-ref url="/pages/-MMuiyOkhGU1N1EtCIG7" %}
[Product Export](/product-export/data-transfer)
{% endcontent-ref %}


# A/B Testing

We ❤️ data driven

## General Info

Like us, many clients also follow the approach of "Data Driven Development". A/B tests are essential for this. \
In order for our platform to be able to generate reliable KPIs, it is important that widgets used in an A/B test are not simply hidden (**display:none**), but are not integrated in the DOM at all.&#x20;

This is not always so easy to do, but it is essential to get a reliable data basis. Just contact us and we will find a solution together!

## Sync your and our A/B test

In order to sync the A/B test variant (or test group) allocation from your shop and our solution you can just add it to the 8.SDK Web configuration.

```javascript
// 8.SDK Web configuration for Shop's A/B test test group
<script type="text/javascript">
  window._eightselect_shop_plugin = window._eightselect_shop_plugin || {};
  window._eightselect_shop_plugin.testgroup = 'A'; // set to whatever value you use to identify your test group or variant
</script>

// 8.SDK Web
<script type="text/javascript">
    ...
</script>

```


# Single Page Application

If your shop is powered by a SPA you have to make sure to "tell" the SDK about updated widget configurations.

Any widget that has some kind of dynamic configuration that will influence wich content is shown has to be replaced whenever the configuration changes. After that is done the SDK has to be informed about the change.&#x20;

{% hint style="info" %}
In the future the SDK will get smarter and recognise widget configurations automatically. For now it is necessary to go the extra mile and rerender the widget. ❤️
{% endhint %}

## How to "tell" the SDK about updated widget configurations

### When is this necessary?

Some examples for 8.SET

* the product variant (other than size) is changed, for example if your customers can select the color or pattern as a variant of a product
* the product completely changes, i.e. if the customer navigates to another product page

### How to implement it?

#### SDK API

```javascript
window._8select.updateWidgetSku('8.SET', 'your-new-sku-here')
window._8select.updateWidgetSku('8.SIMILAR', 'your-new-sku-here')
window._8select.updateWidgetSku('8.SET-Custom', 'your-new-sku-here')
```

#### re-render

* change the configuration for example for the SKU `data-sku="${new-sku}"`
* **required:** also update a property that will make your SPA re-render the component
  * [React](https://reactjs.org/docs/reconciliation.html#keys): `key={new-sku}`&#x20;
  * Vue.js: `:key={new-sku}`
* after the component is rendered call `_8select.initCSE()`

## Navigate to product page via SPA router

Inside our widgets a user can click on a product and will be redirected to the corresponding product page in your shop. This is done by changing the browsers location, i.e. `window.location.href`.

We can configure most widgets to make use of your SPA router if you provide a way to access the functionality via JavaScript. For example we could call something like `window.myFancyRouter.push("/path/to/product/user/wants/to/visit.html")`

{% hint style="info" %}
We have the full deeplink of the product available, i.e. can call your function with the path only or if necessary with the full URL.
{% endhint %}

It is a good idea to also put the function or a reference to it inside the SDK shop configuration object.

```javascript
// 8.SDK Web configuration for Shop's Navigation API
<script type="text/javascript">
  window._eightselect_shop_plugin = window._eightselect_shop_plugin || {};
  window._eightselect_shop_plugin.navigateTo = ...
</script>

// 8.SDK Web
<script type="text/javascript">
    ...
</script>
```


# Tag Manager

If you want to integrate 8SELECT via a tag manager like Google Tag Manager you can do this for the frontend part.\
But be aware that this will increase load times because the Tag Manager and the corresponding tag has to be loaded first.\
Moreover you have to have access to the necessary product data, e.g the [SKU](/product-export/base-data) or [main-SKU](/product-export/base-data) when on a product page and the SKU for the checkout tracking.\
You also have to be able to react to events like a user changing the active product variant, e.g. color in order to update our widget with the new product [SKU](/product-export/base-data) or [main-SKU](/product-export/base-data).


# Data Privacy / Cookies / GDPR

8SELECT does store some data in the browser that are required in order for the 8SELECT recommendation engine to work properly. \
**None are subject to consent according to the GDPR.**

## Cookies

Values stored in the browser and send to a server with each request that is made.

<table data-full-width="true"><thead><tr><th width="134">Name</th><th width="111">Type</th><th width="517">Description</th><th width="75">TTL</th><th>Domain</th></tr></thead><tbody><tr><td><code>_8s_cid</code></td><td>Functional</td><td><p>A random anonymous value.<br></p><p>Used to connect interactions with recommendation content and order lines. The base for 8SELECT commission based pricing model.<br></p><p>Will be refreshed during every visit.<br></p><p><strong>Not subject to consent according to the GDPR.</strong></p></td><td>730 days</td><td>Your second level domain, <br>e.g. <code>shop.com</code></td></tr></tbody></table>

## Local Storage

Values stored in the browser and only readable in the browser. Stored until user deletes values on purpose.

<table data-full-width="true"><thead><tr><th width="252.33333333333337">Name</th><th width="121">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_8select_cse_config</code></td><td>Functional</td><td>Used for various settings regarding the 8SELECT recommendation engine. <br>For example for configuration of test variants during A/B tests.<br><br><strong>Not subject to consent according to the GDPR.</strong></td></tr></tbody></table>

## Session Storage

Values stored in the browser and only readable in the browser. Stored until user ends browsing session and deleted automatically afterwards.

<table data-full-width="true"><thead><tr><th width="130.33333333333337">Name</th><th width="113">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>_8s_sid</code></td><td>Marketing</td><td><p>A random anonymous value.<br></p><p>Used to analyse interaction with our content in order to optimise the content that a user will see within a browsing session.</p></td></tr></tbody></table>


# 8.SIMILAR

Show similar products for currently viewed product.

![similar shoes](/files/-MULCJIn0dMAaYgDFDde)

## Widget-Element

### **Show alternative products for a given SKU or Main-SKU**

{% code overflow="wrap" fullWidth="false" %}

```markup
<div data-8select-widget-id="8.SIMILAR" data-touchpoint="product" data-sku="insert-sku-of-currently-viewed-product"></div>
```

{% endcode %}

* `data-touchpoint` is a static attribute - find more about it under [touchpoints](/widgets/touchpoints)
* `data-sku` is a dynamic attribute
  * its value is the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) of the product variant the customer is viewing in the shop
  * the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) has to be the same value that is used in the [product export](/integration/produkt-export)
  * in case the viewed variant is changed, for example via a color drop-down the value has to be updated via `_8select.updateWidgetSku()`

### Update SKU in case of non-size variant change

{% hint style="warning" %}
The played out content is specific to visual variants such as colour or pattern. When the visual variant changes the widget has to be updated!
{% endhint %}

The 8SELECT content is specific to visual variants such as colour or pattern. \
To retrieve the content of the currently selected visual variant, the current SKU must be passed to the widget when the visual variant is changed. For example if a customer is currently viewing the red variant of a shirt and changes to the blue variant, the 8.SDK has to be informed about the changed SKU to load content for the changed visual variant.\
\
This must not happen when changing the size, as the content is size-independent.

```javascript
window._8select.updateWidgetSku('8.SIMILAR', 'your-new-sku-here')
```


# 8.SET

Automatic cross-selling content - including alternative products.

![8.SET with add-to-cart functionality active](/files/95qZWGR67Y5NP82nNT0i)

## Widget-Element

### **Show complementary products for a given SKU or Main-SKU**

{% code overflow="wrap" %}

```markup
<div data-8select-widget-id="8.SET" data-touchpoint="product" data-sku="insert-sku-of-currently-viewed-product"></div>
```

{% endcode %}

* `data-touchpoint` is a static attribute - find more about it under [touchpoints](/widgets/touchpoints)
* `data-sku` is a dynamic attribute
  * its value is the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) of the product variant the customer is viewing in the shop
  * the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) has to be the same value that is used in the [product export](/integration/produkt-export)
  * in case the viewed variant is changed, for example via a color drop-down the value has to be updated via `_8select.updateWidgetSku()`

### Update SKU in case of non-size variant change

{% hint style="warning" %}
The played out content is specific to visual variants such as colour or pattern. When the visual variant changes the widget has to be updated!
{% endhint %}

The played out content is specific to visual variants such as colour or pattern. \
To retrieve the content of the currently selected visual variant, the current SKU must be passed to the widget when the visual variant is changed. For example if a customer is currently viewing the red variant of a shirt and changes to the blue variant, the 8.SDK has to be informed about the changed SKU to load content for the changed visual variant.\
\
This must not happen when changing the size, as the content is size-independent.

```javascript
window._8select.updateWidgetSku('8.SET', 'your-new-sku-here')
```


# 8.SET Custom

Create product sets from model shootings / mood photography. Simply put model photo and the visible SKUs together. Alternative products are delivered automatically.

<div><figure><img src="/files/UgqUOOs2t1hR12CP4Hls" alt=""><figcaption></figcaption></figure> <figure><img src="/files/jj3OLStZDBMeZ9peb2f1" alt=""><figcaption></figcaption></figure></div>

## Widget-Element

### Content for a given SKU or Main-SKU

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET-Custom" data-touchpoint="content" data-sku="insert-sku-of-currently-viewed-product"></div>
```

{% endcode %}

* `data-touchpoint` is a static attribute - find more about it under [touchpoints](/widgets/touchpoints)
* `data-sku` is a dynamic attribute
  * its value is the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) of the product variant the customer is viewing in the shop
  * the [SKU](/product-export/base-data/details#sku-sku) or [Main-SKU](/product-export/base-data/details#main-sku-main-sku) has to be the same value that is used in the [product export](/integration/produkt-export)
  * in case the viewed variant is changed, for example via a color drop-down the value has to be updated via `_8select.updateWidgetSku()`

### Show specific set

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET-Custom" data-touchpoint="content" data-set-id="insert-id-of-your-set"></div>
```

{% endcode %}

* `data-touchpoint` is a static attribute - find more about it under [touchpoints](/widgets/touchpoints)
* `data-set-id` is a static or dynamic value
  * its value is the id of the custom set you created


# Touchpoints

Simple version of 8.SET Compose with limited user interaction - for example for a cart layer

You can use any widget on any kind of page in your shop. In order to identify which page the widget is on you need to add another data attribute - `data-touchpoint`.

Eligible values are `product`, `cart-layer`, `cart`, `content`, `category`, `search`.

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET" data-sku="insert-sku-of-currently-viewed-product" data-touchpoint="cart-layer"></div>
```

{% endcode %}

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Product Page</strong></td><td>Classic touchpoint - the product page.</td><td><a href="/files/ePUzohsZTEWhGqJ9w3gA">/files/ePUzohsZTEWhGqJ9w3gA</a></td><td><a href="/pages/7bceSw9e3DLcSbP09MIy">/pages/7bceSw9e3DLcSbP09MIy</a></td></tr><tr><td><strong>Cart Layer</strong></td><td>Show complementary products for the product that was added to the cart.</td><td><a href="/files/rQSKR7ZwQuZA1Sri7nR0">/files/rQSKR7ZwQuZA1Sri7nR0</a></td><td><a href="/pages/3YwbY4o5KbU2tSI1IjPB">/pages/3YwbY4o5KbU2tSI1IjPB</a></td></tr><tr><td><strong>Cart</strong></td><td>Show complementary products for the cart's content.</td><td><a href="/files/pLKVJ3HWGAYdJ34HWO8X">/files/pLKVJ3HWGAYdJ34HWO8X</a></td><td><a href="/pages/eK5xyQfx2vWiOW98Rxih">/pages/eK5xyQfx2vWiOW98Rxih</a></td></tr><tr><td><strong>Content Page</strong></td><td>Show custom sets with automatic alternatives.</td><td><a href="/files/gH9b49qZyaSM0E7Ln6Ha">/files/gH9b49qZyaSM0E7Ln6Ha</a></td><td><a href="/pages/JXxcbR73NMoseF0qYzJm">/pages/JXxcbR73NMoseF0qYzJm</a></td></tr><tr><td><strong>Category Page</strong></td><td>Show content on category listing</td><td></td><td></td></tr><tr><td><strong>Search result</strong></td><td>Show content on search result page</td><td></td><td></td></tr></tbody></table>


# Product Page

Show content on the product page.

<figure><img src="/files/ePUzohsZTEWhGqJ9w3gA" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SIMILAR" data-sku="insert-sku-of-currently-viewed-product" data-touchpoint="product"></div>
<div data-8select-widget-id="8.SET" data-sku="insert-sku-of-currently-viewed-product" data-touchpoint="product"></div>
```

{% endcode %}


# Cart Layer

Show content on a modal after a product was added to the cart.

<figure><img src="/files/rQSKR7ZwQuZA1Sri7nR0" alt=""><figcaption></figcaption></figure>

## Show complementary products via 8.SET

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET" data-sku="insert-sku-of-currently-added-product" data-touchpoint="cart-layer"></div>
```

{% endcode %}

## Show similar items via 8.SIMILAR

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SIMILAR" data-sku="insert-sku-of-added-viewed-product" data-touchpoint="cart-layer"></div>
```

{% endcode %}

## Integration

1. when a product is added to the cart, add the widget element where you would like to show recommendation content including `data-sku`
2. call `_8select.initCSE()` to let the SDK discover the new widget


# Cart

Show content on the cart overview.

<figure><img src="/files/pLKVJ3HWGAYdJ34HWO8X" alt=""><figcaption></figcaption></figure>

Show complementary products via 8.SET

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET" data-sku="insert-sku-of-any-cart-product" data-touchpoint="cart"></div>
```

{% endcode %}

or show similar items via 8.SIMILAR

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SIMILAR" data-sku="insert-sku-of-any-cart-product" data-touchpoint="cart"></div>
```

{% endcode %}


# Content Page

Show content on landingpages and tell a story.

For example custom content via 8-SET Custom.

<figure><img src="/files/gH9b49qZyaSM0E7Ln6Ha" alt=""><figcaption></figcaption></figure>

{% code overflow="wrap" %}

```html
<div data-8select-widget-id="8.SET-Custom" data-set-id="insert-id-of-custom-set" data-touchpoint="content"></div>
```

{% endcode %}


# Data Transfer

We support two main ways of transferring product data between your shop and our infrastructure.

## Pull

We periodically download your product data files from a given origin using any of the following protocols:

* HTTP(S)
  * requires a `"Content-Type"` header indicating the feed format, e.g. `text/csv`
  * supports optional `gzip` compression, if specified in a `"Content-Encoding"` header
* (S)FTP
  * requires a file extension indicating the feed format, e.g. `.csv`
* AWS S3
  * requries a `"Content-Type"` metadata indicating the feed format, e.g. `text/csv`

Each of these protocols can be used with and without credentials — if you have non-standard requirements, just talk to us and we'll find a way :yellow\_heart:

## Push

You upload your product data files whenever something updates to our S3 bucket.\
You request a signed upload URL from our API and can then upload your file. You just need to make 2 simple HTTP calls for that.

{% hint style="success" %}
Your files are encrypted on our side and can not be accessed from the outside.
{% endhint %}

![](/files/KHb4l8fLmbouYhwWzmwZ)

### How it works

1. Send a HTTP POST request to `api.8select.io/feeds/uploads` to retrieve a pre-signed upload request, i.e. a JSON object containing the following properties:
   * `headers`: A map of string key-value pairs
   * `method`: A HTTP method, e.g. `PUT`
   * `url`: A presigned URL
2. Send a second HTTP request using the `method` and `url`, **and including all** `headers` returned by the previous request, as well as the file to be uploaded in the HTTP body.

### API

#### get a pre-signed request for product feed upload

<mark style="color:green;">`POST`</mark> `https://api.8select.io/feeds/uploads`

#### Headers

<table><thead><tr><th width="208.296875">Name</th><th width="180.7313232421875">Type</th><th>Description</th></tr></thead><tbody><tr><td>x-api-id<mark style="color:red;">*</mark></td><td>String</td><td>Your API ID provided by us.</td></tr><tr><td>x-api-secret<mark style="color:red;">*</mark></td><td>String</td><td>Your API SECRET provided by us.</td></tr><tr><td>content-type<mark style="color:red;">*</mark></td><td>application/json</td><td></td></tr></tbody></table>

#### Request Body

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

<table><thead><tr><th width="177.1312255859375">Name</th><th width="131">Type</th><th>Description</th></tr></thead><tbody><tr><td>identifier<mark style="color:red;">*</mark></td><td>String</td><td>The field by which a record can be uniquely identified, e.g. <code>sku</code> or <code>product_id</code> </td></tr><tr><td>format<mark style="color:red;">*</mark></td><td>Object</td><td><pre class="language-json" data-overflow="wrap"><code class="lang-json">{ 
  "options": { 
    "delimiter": "," 
  }, 
  "type": "csv" 
}
</code></pre></td></tr><tr><td>delta</td><td>Boolean</td><td><p>If set to <code>false</code> we will mark all products that are not present in the feed as deleted.</p><p>If set to <code>true</code> we will just update the products contained in the feed and keep all other products as is.</p><p></p><p>That way you can push so called delta-feeds that only include changes and save ressources and you and our side.</p><p></p><p><code>true</code> or <code>false</code><br><br>defaults to <code>false</code></p></td></tr></tbody></table>
{% endtab %}

{% tab title="example" %}

```json
{
  "delta": true,
  "format": {
    "options": {
      "delimiter": ","
    },
    "type": "csv"
  },
  "identifier": "product_id"
}
```

{% endtab %}
{% endtabs %}

#### Response

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

```typescript
{
    "headers": {
        "Content-Type": "text/csv; charset=utf-8", // depending on the request parameters
        "X-Amz-Meta-Delimiter": ",", // depending on the request parameters
        "X-Amz-Meta-Sku-Field-Name": "sku", // depending on the request parameters
        ... // could be more
    },
    "method": "PUT",
    "url": "https://s3.eu-central-1.amazonaws.com/..."
}
```

{% endtab %}
{% endtabs %}

#### API usage example via cURL

This is just for demonstration purposes. You can use this method to make a test upload. For production use you should do this programatically with code on your server. :sunglasses:

Replace `your-api-secret` and `your-api-secret` with your credentials.

{% tabs %}
{% tab title="get presigned URL" %}
{% code overflow="wrap" lineNumbers="true" %}

```bash
curl -v -X POST "https://api.8select.io/feeds/uploads" -d '{ "identifier": "sku", "format": { "type": "csv","options": { "delimiter": ","}}}' -H "x-api-id:your-api-id" -H "x-api-secret:your-api-secret" -H "content-type:application/json"
```

{% endcode %}
{% endtab %}

{% tab title="upload file" %}
{% code overflow="wrap" lineNumbers="true" %}

```bash
curl -X PUT -T /path/to/your/file -H "Content-Type: text/csv" -H "X-Amz-Meta-Delimiter:," -H "X-Amz-Meta-Delta:false" -H "X-Amz-Meta-Identifier:sku" \
        "https://s3.eu-central-1.amazonaws.com/......"
```

{% endcode %}
{% endtab %}
{% endtabs %}


# File Format

We currently only support CSV format.\
Each line will hold a purchasable product variant aka SKU.

{% hint style="info" %}
You can not provide files in CSV format? No problem, talk to us and we will find a solution. :heart:
{% endhint %}

## CSV Format

* Column separator is a semicolon `;`
* Wrap all values in the text qualifier double-quote `"`
  * If your text includes double-quotes those need to be escaped with another double-quote \
    `"T-Shirt ""Santa"""`
* For multi-value fields use a comma `,` to separate values
* Encoding is UTF-8

## Contents

A product export can either contain all of your product catalogue or only parts of it.

If you are able to send updates on demand or can send a delta (i.e. only include changed products since the last export) you can do so as well. If not you can just send all products.

In case you can only export in stock products that is also fine.\
We treat products that where in an export in the past but are not inside an export for more than 24 hours as out of stock. So in general it is better to also include out of stock products in an export, to reflect stock changes immediately.

{% hint style="warning" %}
You should send us a full export every 24 hours - that way we can make sure everything is in sync.
{% endhint %}

You can also use different feeds in terms of columns. For example one with all fields and one with only stock and price. That is a best practice in case you can not provide a delta feed but want to reduce ressources to create the feed on your side as much as possible.

## Examples

Here are two examples for you.:bulb:

{% file src="/files/A7S2Qw2Yl3Jec5wiqTHY" %}
full example
{% endfile %}

{% file src="/files/jwzCpCqvNYal9jE2aQi1" %}
stock and price update example
{% endfile %}

{% hint style="warning" %}
Prices and stock levels can change very fast. To ensure that 8SELECT and the shop are in sync we recommend that you deliver one full export every 24 hours and an extra stock-price-update more often - up to every 15 minutes.
{% endhint %}


# Base Data

Base product data we need to playout any content

In order to be able to display the products meaningfully in the widgets, a basic set of data must be transmitted.&#x20;

### Required properties

If a property has multiple values those should be separated by a comma `,`.

* SKU
  * primary identifier used in widgets and tracking
* Main-SKU (Parent-SKU)
  * identifier for group of product variants (size only)
* Model
  * identifier for group of product variants (colors etc.)
* Stock
  * current stock level or flag 0/1
* Variant
  * name of variant (size) - for example `L, M, 32/34, 42, 150 ml`
* Name
  * product name
* Category paths
  * breadcrumbs used in the shop - for example `Women > Clothes > Jackets > Blazer`
  * if product is in multiple paths use a comma `,` as a separator
* Brand
* Retail price
* Discount price
* Lowest price in the last 30 days\*
  * only if required by law in your country
* Deeplink to product variant in shop
  * either to variant where the user then needs to choose the size or directly to the SKU
* Image
  * Main image in maximum resolution. Used for data tagging via image recognition.
  * For an optimal recognition use the cut out image (hollow man photography) without any other objects / persons.
* Thumbnail
  * Main image in smaller resolution. Used for widget content.
  * Use image with least possible fillup / whitespace to optimize the presentation in the widgets
  * For an optimal presentation use the cut out image (hollow man photography) without any other objects / persons.

{% hint style="danger" %}
In case you can not provide a thumbnail, we deliver image thumbnails from our CDN and cache images heavily. If you change images you have to also change the image URL in order to invalidate the CDN cache!&#x20;
{% endhint %}

{% hint style="info" %}
In order to access our Fashion Content Pool, further data is required. See [**Fashion Content Pool**](/product-export/fashion-content-pool).
{% endhint %}

{% content-ref url="/pages/-MMz3n4u5FawDgUjSwlJ" %}
[Details and examples](/product-export/base-data/details)
{% endcontent-ref %}


# Details and examples

## SKU (sku)

The SKU is unique. The SKU is the main identifier for a specific item (buyable physical variant) in a shop.

Variant (SKU): Shirt Arie - color blue - size M

### Examples

```
8277-480-01
8febdd77-c6a6-40f6-b322-9620680c7bc2
42
A-2-21
```

{% hint style="warning" %}
The SKU is used to find content for an item, to add an item to the shopping cart and to match purchases with interactions in our widgets. \
This means that this value must also be available in the shop for the widgets and in the checkout.
{% endhint %}

## Main-SKU (main-sku)

The Main-SKU is unique, it contains the model and colour. The Main SKU is the main identifier for an item (parent item) in a shop.&#x20;

The Main-SKU is used to establish a relationship between parent items and size variants.\
\
Parent item (Main-SKU): Shirt Arie - color blue\
Variant (SKU): Shirt Arie - color blue - size M

### Examples

```
8277-480
Shirt-blue
```

## Model (model)

A model refers to the basic product. A model can exist in several versions that differ, for example, in size, colour or pattern.

The model `Arie` (8277) exists in three different colors: `blue` (480), `red` (481), `yellow` (482) and in four different sizes: `S` (01), `M` (02), `L` (03), `XL` (04). All combined will make up to 12 different variants. Each variant has its own SKU.\
\
Model: Shirt Arie\
Parent item (Main-SKU): Shirt Arie - color blue\
Variant (SKU): Shirt Arie - color blue - size M

### Examples

| model      | main-sku              | sku                          |
| ---------- | --------------------- | ---------------------------- |
| 8277       | 8277-480              | 8277-480-01                  |
| shirt-arie | shirt-arie-color-blue | shirt-arie-color-blue-size-m |

## Stock (stock)

The value provides information about the availability of a variant. It can be a flag (0/1) or the quantity of available items.

### Examples

available

```
1
3
12
```

not available

```
0
```

## Variant (variant)

For example the size of the item.

### Examples

```
38
L
32/32
32 L
M-L
30ml
```

## Name (name)

Default name for the item as it is normally used on the product page.

### Examples

```
Desigual Sweatshirt
Evelyne GM 33
```

## Retail price (retail-price)

The original price or RRP. \
Usually shown as a crossed-out price in the shop.&#x20;

Without currency and in cents.

### Examples

```
8000
19939
```

## Discount price (discount-price)

The price at which the item is sold.&#x20;

Without currency and in cents.

### Examples

```
6995
13750
```

## Product URL (deeplink)

Deep link that leads directly to the detail page in the shop of the variant.

### Examples

```
https://www.ambellis.de/desigual-sweatjacke-8277480.html?sku=8277387
https://www.bettybarclay.com/de/vera-mont-cocktail-kleid-21254528.html?farbe=burnished-ros&groesse=38
```

## Image (image)

* Main image in maximum resolution. Used for data tagging via image recognition.
* For an optimal recognition use the cut out image (hollow man photography) without any other objects / persons.

### Examples

```javascript
https://ambellis.scene7.com/is/image/ambellis/ext/8277480-01.jpg?$rtf\_amb\_prod-main-zoom\_xl$

https://cdn.8select.io/image1.jpg
```


# Fashion Content Pool

We provide a huge amount (currently 5 figures) of pre defined product sets created by our fashion experts.\
Depending on your assortment those will cover most of your products with content. We constantly improve the existing sets andd new sets according to current trends and add new sets for other assortments.

In order to access the Fashion Content Pool, we need certain product properties so that our system can normalise the data and enrich the products with further properties.&#x20;

As a mimimum requirement the export must contain the following values:

* category
* description
* brand
* color

On top of that there is no limit. The more properties are transmitted, the more precisely the products are enriched with further properties. \
The combination of products inside the sets can be curated more precisely the more properties there are.

{% file src="/files/-MSCd4zhrVROEVFGKAoR" %}
fashion content pool full example
{% endfile %}

{% file src="/files/-MSCdG-3yDeZ\_yXaWQjh" %}
fashion content pool minimum example
{% endfile %}

{% file src="/files/-MSCdRPLsdhoKkXrAYVz" %}
stock and price update example
{% endfile %}

{% hint style="warning" %}
Prices and stock levels can change very fast. To ensure that 8SELECT and the shop are in sync we recommend that you deliver one full export every 24 hours and an extra stock- price-update more often - up to every 15 minutes.
{% endhint %}

{% content-ref url="/pages/-MMz3\_4etaDpPLp43Yau" %}
[Details and examples](/product-export/fashion-content-pool/details)
{% endcontent-ref %}


# Details and examples

{% hint style="info" %}
Values must be in plain text except for description.&#x20;

Some properties are **required**.
{% endhint %}

## Description (beschreibung) - required

The description text for the article. Often used in the shop on the product detail page. HTML format is allowed here.

### Examples

```markup
<p>
Diese auffällig gemusterte Sweatjacke von Desigual zeigt sich mit sportiven 
Raglanärmeln und Kapuze mit Tunnelzug. Ärmelsaum und Saum sind elastisch 
gestaltet.<br><br>    
- modisch nach innen gezogenen Seitennähte<br>  
- zwei Nahttaschen auf Hüfthöhe<br>  
- Rückenteil modisch verlängert<br>  
- Rückenlänge bei Gr. S (36): ca. 64 cm<br>  
- unser Model trägt Größe S (36) bei einer Körpergröße von 176 cm<br><br>   
Material: 51% Baumwolle, 45% Polyester, 4% Elasthan<br><br>    
---Pflegehinweise---<br>  
- Maschinenwäsche bei 30°, von links<br>  
- nicht für den Trockner geeignet<br>
</p>
```

## Color (farbe) - required

The exact colour name of the item.

### Examples

```
rot
grün
gelb
schwarz-rot-gold
Burnished Rosè
```

## Category (kategorie1) - required

Description of the article groups. Often used in the shop's navigation and as breadcrumbs. The full category path will work best.

### Examples

```
Herren - Outdoor Bekleidung - Outdoorhosen - Hardshellhosen
Damen - Blusen & Tuniken
Women - Clothes - Skirts
Men - Clothes - Underwear & Nightwear - Sleepwear
```

## Brand (marke) - required

Brand name or manufacturer of an item.

### Examples

```
JP1880
HUGO BOSS
Betty Barclay
Jimmy Choo
Gucci
```

## Heel height (absatzhöhe)

Heel height of shoes with unit of measurement.

### Examples

```
5,5cm
Absatzhöhe in mm: 60
```

## Department (abteilung)

Classification of the assortments according to target groups. Often used in the shop's navigation.

### Examples

```
DOB
HAKA
KIKO
BESPO
Damen
Herren
Kinder
```

## Sleeve length (aermellaenge)

Especially for upper garments, the length of the sleeves.

### Examples

```
normal
extra-lange Ärmel
ärmellos
3/4 Arm
extra kurzer Arm
```

## Specification (auspraegung)

Important information, especially for sports and outdoor activities, which helps to classify the article in the range.

For backpacks, for example, volume `30-55 litres`, for ski poles the size information in units of measurement `160 to 175cm` or for special versions `left-handed`.

### Examples

```
30-55 Liter
Körpergröße 160 bis 175cm
Linkshänder
Rucksackmaße 40 cm x 28 cm x 18 cm
```

## Related items (baukasten)

SKU for a direct link to 1:1 related items. For example for modular suits.\
On top you can provide a [group](/product-export/fashion-content-pool/details#group-gruppe).

### Examples

```
123-456-789
```

## Assortment (bereich)

A shop may allocate products to different assortments.

### Examples

```
Mode
Outdoor
Kosmetik
Trachten
Lifestyle
DIY
```

## Additional description (beschreibung2)

Additional information about the product, technical description, short description or keywords.

### Examples

```
Gewicht=200 g Gewogen=Gr. L/31 Material=100% Nylon (Ripstop) mit Gore-Tex-Membran (PTFE)
```

## Details (detail)

Any details of the article worth mentioning.

### Examples

```
Reißverschluss seitlich am Saum
Brusttasche
Volants
Netzeinsatz
Kragen in Kontrastfarbe
```

## EAN (ean)

Standardised unique material number according to European Article Number (EAN) or Unified Product Code (UPC).

### Examples

```
5027793879236
```

## Features (eigenschaft)

Specially designed for sports and outdoor use notes on the field of application.

### Examples

```
5 °C
Schlafsack geeignet für Temparaturbereich 1 °C bis -16 °C
```

## Colour spectrum (farbspektrum)

The colour spectrum refers to the main colour in the product and allows product sets across different colours from the same spectrum. Often used as a search filter in the shop.

### Examples

```
Schwarz Grau
Braun Beige
Rot
Blau
Gelb Orange
```

## Filling quantity (fuellmenge)

Refers to the quantity of the item's contents, for example in the case of perfume.

### Examples

```
200ml
0,5 Liter
3kg
150 Stück
```

## More features (funktion)

Describes material functions and properties.

### Examples

```
kratzfest
wasserdicht
trocknet schnell
schnelltrocknend
atmungsaktiv
100% UV-Schutz
pflegeleicht
bügelleicht
körperformend
```

## Group (gruppe)

Denotes an identifier for items that directly belong together. You can also add a relation via the [related items](/product-export/fashion-content-pool/details#related-items-baukasten) property.

For example, bikini top "Aloha" and bikini bottom "Aloha" make the product `4242`. Construction jacket "Ernie" and construction trousers "Bert" make the product group `E&B`.

A group can also consist of more than two items, e.g. mix & match group `Hawaii` consists of three bikini tops and two bikini bottoms.

### Examples

```
12345
E&B
Hawaii
Gruppe-9000
```

## Subcategory (kategorie2)

Subcategory used in the shop.

For example the category is "Damen - Blusen & Tuniken" and the subcategory is "Hemdblusen".

### Examples

```
Regenhosen
Hemdblusen
```

## Extra category (kategorie3)

Extra category used in the shop. Either another subcategory or a completely different category used for this item.

For example the category is "Damen - Blusen & Tuniken", the subcategory is "Hemdblusen" and the extra category is "Kurzarmblusen".

Another example: The category is "Men - Clothing - Jackets", the subcategory is "Jackets" and the extra category is "Leather Jackets".

### Examples

```
Kurzarmblusen
Lederjacken
Parkas
Blousons
Freizeit-Hemden
```

## Children's clothing (kiko)

Classification according to target groups specifically for children's ranges. Often used in the shop's navigation.

### Examples

```
mädchen
jungen
baby
kleinkinder
```

## Collar shape (kragenform)

Especially for upper garments, the description of the collar or neckline.

### Examples

```
Rollkragen
V-Ausschnitt
Blusenkragen
Haifischkragen
Rundhalsausschnitt
```

## Material (material)

Material composition of a product.

### Examples

```
98% Baumwolle, 2% Elasthan
100% Nylon (Ripstop) mit Gore-Tex-Membran (PTFE)
```

## Pattern (muster)

### Examples

```
uni
kariert
floral
einfarbig
gestreift
Blumenmuster
einfarbig-strukturiert
```

## Short name (name2)

Often used as a short name in list views - e.g. `Freizeit-Hemd` or for search engine optimization.

### Examples

```
Regenhose
Bluse
```

## Miscellaneous (sonstiges)

Additional item information that cannot be assigned to a specific attribute.

### Examples

```
abgenähte Taschen
Ripstop
Natürlich
Bio
```

## Upper material (obermaterial)

Rudimentarily distinguishes the fabric. Essential material of the article.

Only fabrics with special optical properties are distinguished.

### Examples

```
baumwoll-denim
velourlederoptik
Wildleder
Denim
Edelstahl
Gewebe
Strick
Jersey
Sweat
Crash
Boucle
Grobstrick
```

## Fit (passform)

In relation to the body shape, is often used for shirts, jackets and suits.

### Examples

```
modern fit
schmal
bequeme Weite
slim-fit
regular-fit
comfort-fit
körpernah
```

## Special article groups (rubrik)

Designates special article groups. Often used in the shop as a search filter or in the navigation.

### Examples

```
Große Größen
Schwangerschaftsmode
Umstandsmode
Stillmode
Übergrößen
```

## Season (saison)

Describes to which season or seasonal collection the item belongs.

### Examples

```
Winter
Sommer
Winter 17
Sommer 2018
HW18/19
```

## Cut (schnitt)

In reference to the form of the article. Is often used for shirts, trousers and shoes.

### Examples

```
Bootcut
gerades Bein
Oversized
spitzer Schuh
knielang
7/8
```

## Series (serie)

Series are used to identify article families or special editions.

### Examples

```
Mountain
Mountain Professional
Expert Line
```

## Sport type (sportart)

Sports in which the article is used.

### Examples

```
Golf
Bergsteigen
Radfahren
Bike
```

## Style (stil)

Style of the article.

### Examples

```
Business
Casual
Ethno
Retro
```

## Exact model (typ)

The exact model designation describes the product type. For example "Desertboot" instead of "Boot" or "Rain trousers" instead of "Trousers".

### Examples

```
Desertboot
Regenhose
Hemdbluse
```

## Closure (verschluss)

Describes closure types of an item.

### Examples

```
Knöpfe
geknöpft
Reißverschluss
Druckknöpfe
Klettverschluss
Haken&Öse
```

## Washing (waschung)

Optical effect of the material, mostly used for denim.

### Examples

```
used
destroyed
bleached
vintage
stone washed
```


# Image Bot

We use the product images not only in our widgets but also to enrich the product data via image recognition. To do this, it is necessary to download the product images.

We do this via our image bot.

To ensure a smooth operation of the products, our image bot must have access to the product images.

The image bot uses the following user agent:

```http
8SELECT-imagebot/1.0 (+https://www.8select.com/bot)
```

The origin IP is not fixed. If it helps here is the [possible IP range](https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html).


# Demo-Integration

To test the integration, the 8.SDK can be inserted into the shop via the (Chrome) Developer Tools. For this purpose, there is a demo tenant as well as demo product data and content.&#x20;

Insert and execute the following snippets one by one to show the widget 8.SET Compose at the top of the current page.

### Add 8.SDK Web in demo mode to your shop

```javascript
// Load 8.SDK

(function(d, s, w) {
  var apiId = 'db54750f-80fc-4818-9455-30ca233225dc';
  window.eightlytics || function (w) {
      w.eightlytics = function () {
        window.eightlytics.queue = window.eightlytics.queue || [];
        window.eightlytics.queue.push(arguments);
      };
  }(w);
  var script = d.createElement(s);
  script.src   = 'https://wgt.8select.io/' + apiId + '/loader.js';
  var entry = d.getElementsByTagName(s)[0];
  entry.parentNode.insertBefore(script, entry);
})(document, 'script', window);
```

### Add a widget to your shop

You can add any widget via its id like described in the section [WIDGETS](https://docs.8select.io/widgets/).

```javascript
// Add 8.SET Widget
_8select.demo('8.SET')
```

{% hint style="warning" %}
Currently only the widget `8.SET-Compose` supports the demo mode.
{% endhint %}


# Demo-Mode

If you have added the 8.SDK and widgets to your shop you can test the integration before the product export is working and content is delivered. To activate the demo mode there are two options:

* URL parameter `8S-demo=true`
* set `window._eightselect_config = { isDemoMode: true }`

{% hint style="warning" %}
Currently only the widget `8.SET-Compose` supports the demo mode.
{% endhint %}

![8.SET Compose 1.0 in demo mode](/files/-MLMixoQSPVjz2tBdaS5)


# Changelog

## [2.2.0 - 2025-12-03](/api/changelog/2.2.0)

### Implemented Features

* You can now request a simplified version of 8.SET and 8.SET Custom combined

[Go to detailed version description.](/api/changelog/2.2.0)

## [2.1.0 - 2025-09-04](/api/changelog/2.1.0)

### Implemented Features

* You will now get back more than 1 8.SET Custom set.

[Go to detailed version description.](/api/changelog/2.1.0)

## [2.0.0 - 2024-07-25](/api/changelog/2.0.0)

### Implemented Features

* You can now request any type of content via the API
* The schema completely changed
* You can now not only get the product id but also the properties like brand, name, prices etc.
  * To ensure that you show the most recent data you should still make an extra roundtrip to your own database and fetch the most recent data to render your UI

[Go to detailed version description.](/api/changelog/2.0.0)

## [1.0.1 - 2021-04-27](/api/changelog/1.0.1)

### Fixed Bugs

* An empty result is now empty, before it contained an edge with `null` values

[Go to detailed version description.](/api/changelog/1.0.1)

## 1.0.0 - 2021-03-12

### Implemented Features

* Initial release with support for 8.SET Compose


# 2.2.0

{% hint style="success" %}
We heard your feedback and combined 8.SET and 8.SET Custom into one query that already creates useable product sets. :tada:Your feedback is always welcome and helpful! :heart:
{% endhint %}

## Features

We have added a new query `productSets` that will return a simplified version of the 8.SET and 8.SET Custom content combined. The response can be used as as if you want to show paginated product sets.

Check out the [example page](/api/examples/8.set-simplified).

Also keep in mind that the [schema](/api/general/graphql-schema) of the respective nodes is different compared to the classic `customSets` or `matchingProductClusters` queries.


# 2.1.0

## Features

You can now query more than 1 custom-set via the `product.customSets` query and the `first` parameter.

<pre class="language-graphql" data-expandable="true"><code class="lang-graphql">query {
  product(id: "8S-DEMO-Polohemd-1") {
<a data-footnote-ref href="#user-content-fn-1">    customSets(first: 5) {</a>
      edges {
        node {
          id
          title
          description
          images {
            edges {
              node {
                url
              }
            }
          }
          products {
            id
            similarProducts(first: 2) {
              edges {
                node {
                  id
                }
              }
            }
          }
        }
      }
    }
  }
}
</code></pre>

[^1]: this will return the first 5 sets


# 2.0.0

## Features

We have added all of our content types and simplified the query structure. You can now query all content types with 1 simple query.

We have deprecated the existing `setCompose` query and will remove it later this year or as soon as all customers have migrated to the new query.


# 1.0.1

## Fixed Bugs

### An empty result is now empty, before it contained an edge with `null` values

The `productSets()` query result would previously always include at least one —\
potentially empty — node. This caused unintended and confusing behaviour when\
an empty list would have been expected, e.g. there are no sets for the given\
product or the given product does not exist.

**Example**

```
query MyQuery {
  productSets(input: {queryType: SKU, value: "<non-existent-product-sku>"}) {
    edges {
      node {
        title
      }
    }
  }
}
```

**8.API 1.0.0 had returned**

```
{
  "data": {
    "productSets": {
      "edges": [
        {
          "title": null
        }
      ]
    }
  }
}
```

**8.API 1.0.1 returns**

```
{
  "data": {
    "productSets": {
      "edges": []
    }
  }
}
```


# General

GraphQL-based API for all 8SELECT products

{% hint style="info" %}
&#x20;If you are interested in using 8.API, please speak to your account manager.
{% endhint %}


# Introduction

At 8SELECT we are working towards providing centralized access to all of our products using GraphQL as the main API technology.

## What is GraphQL?

GraphQL is a query language for clients to fetch data they need from an API. It is defined through a [**specification**](http://spec.graphql.org), which describes how data should be requested and formatted in a response.&#x20;

GraphQL is strongly typed and well structured. That means, an API will not only define **and guarantee** what type of data each field in its schema can be, but also define the links and relationships between any complex objects. This enables queries to fetch exactly the information they need by specifying either a single object or traversing any links in the structure to request related information in a single query.

### Why use GraphQL?

At 8SELECT we use GraphQL primarily because it enables developers to make API calls which gets them exactly what data they need in the simplest way possible. Since it is a well structured schema you can fetch data and know that you will get the data back in a guaranteed format and because of that, tooling can make development significantly easier.

### How to query GraphQL?

Most commonly GraphQL can be queried by making HTTP `POST` requests to its respective endpoint. At 8SELECT this endpoint currently is `https://api.8select.io/graphql`. To make a request you can use cURL or any other HTTP compatible client. There are also various GraphQL specific clients available in different programming languages.

You get query content for a given product identifier or query a specific custom-set.

Assuming you have a product with the SKU `8S-DEMO-Polohemd-1` in your catalogue,  you could use the following example to query a list of similar products.

```graphql
product(id: "8S-DEMO-Polohemd-1") {
   similarProducts(first: 5) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

To send this query to the GraphQL endpoint you would use the following request in `curl`

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    similarProducts(first: 5) {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n  }\n}\n"}'
```

And the result will be a list of similar products limited to the first 5

```json
{
  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-7"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-8"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-2"
            }
          }
        ]
      }
    }
  }
}
```

#### Further Information

To find out more about GraphQL in general, read the official [**GraphQL documentation**](https://graphql.org/learn/) and learn about writing efficient [**GraphQL queries**](https://graphql.org/learn/queries/).&#x20;


# Authentication

All API requests must be authorized through your API ID in the HTTP request headers.

{% hint style="info" %}
To obtain your API ID for use with the 8SELECT API, please speak to your account manager.
{% endhint %}

Your API ID is permanently linked to your 8SELECT account. It must be included in every call to the API as a `x-api-id` header to authorize your request:

```
POST /graphql
Host: https://api.8select.io
x-api-id: <Your API ID>
```


# Pagination

Whenever two objects have a one-to-many relationship, the respective field will be paginated using cursor-based, forward pagination.

{% hint style="warning" %}
As pagination is not yet fully implemented, all paginated fields will allow you to limit the returned items via the first argument. However it is not possible to set an offset.

To avoid breaking changes once pagination will be enabled, the respective pattern is already prepared and enforced.
{% endhint %}

## Nodes and Edges

Objects that share a relationship within our API are referred to as **nodes** and their connections as **edges**. To enable pagination, a field representing a one-to-many relationship contains a single `edges` object, which in turn is a list of nodes. Each such `node` object represents a single related item.

According to this structure, you could query a list of similar products:

```graphql
product(id: "8S-DEMO-Polohemd-1") {
   similarProducts {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

The responding result will reflect the same structure:

<pre class="language-json"><code class="lang-json"><strong>{
</strong>  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9"
            }
          },
          ...,
          {
            "node": {
              "id": "8S-DEMO-Polohemd-6"
            }
          }
        ]
      }
    }
  }
}
</code></pre>

## Slicing

An optional parameter `first` can be used to limit the number of items returned in a result list.&#x20;

The following query for example, would allow you to return only a single 8.SET Compose product set for a product with the SKU `123456-7890` in your catalogue:

```graphql
product(id: "8S-DEMO-Polohemd-1") {
   similarProducts(first: 2) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

You can pass an arbitrary positive integer to the `first` argument. Bear in mind though, that the result will always be the same shape, even if you request a single item. So the above query would result in:

```json
{
  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9"
            }
          }
        ]
      }
    }
  }
}
```


# Exceptions

Any errors during resolution of a GraphQL query are returned as part of the API response.

Using GraphQL's introspection capabilities in conjunction with respective tooling allows to avoid many potential errors already during development. However of course, not all failures can be prevented.

Since a complex GraphQL query can involve many separate data sources to resolve various fields, the resolution can fail partially and still succeed in the remaining parts. That's why a response can contain a list of `errors` in addition to a `data` property.&#x20;

## Authorization Error

Unauthorized requests cannot be resolved properly. All top-level fields in the included query will default to `null` and the failed resolutions be reported as part of the list of occured `errors`.

Let's a ssume the following query to fetch similar products:

```graphql
product(id: "8S-DEMO-Polohemd-1") {
   similarProducts(first: 5) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

If this query would be sent in an unauthorized request (notice the missing `x-api-id` header):

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    similarProducts(first: 5) {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n  }\n}\n"}'
```

The result would list the failure:

```json
{
  "data": {
    "product": null
  },
  "errors": [
    {
      "path": [
        "product"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access product on type ProductReference"
    }
  ]
}
```

## Validation Error

For requests with invalid queries you will get back the information about what is wrong.

Let's assume we used a wrong argument in our query, i.e. `sku` instead of `id`:

```bash
curl --request POST \
        --url https://api.8select.io/graphql \
        --header 'Content-Type: application/json' \
        --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
        --data '{"query":"query {\n  product(sku: \"8S-DEMO-Polohemd-1\") {\n    similarProducts {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n  }\n}\n"}'
```

The result would list the failure and hint on which line something is wrong:

```json
{
  "data": null,
  "errors": [
    {
      "path": null,
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": null
        }
      ],
      "message": "Validation error of type MissingFieldArgument: Missing field argument id @ 'product'"
    },
    {
      "path": null,
      "locations": [
        {
          "line": 2,
          "column": 11,
          "sourceName": null
        }
      ],
      "message": "Validation error of type UnknownArgument: Unknown field argument sku @ 'product'"
    }
  ]
}
```


# GraphQL Schema

Full annotated GraphQL introspection schema for reference.

```graphql
schema {
  query: Query
}

type Query {
  """
  Retrieves a single custom set by its id.
  """
  customSet(id: ID!): CustomSet

  """
  References a product by id, which exposes fields for cluster sets,
  custom sets and similar products. `id` can be a SKU or main SKU.
  """
  product(id: ID!): ProductReference

  """
  @deprecated
  Fetch 8.SET Compose product sets for a given trigger product.
  """
  setCompose(
    "An optional number of product sets to fetch."
    first: Int
    "Specifies the trigger product for which to fetch product sets."
    input: SetComposeQueryInput!
  ): SetComposeConnection
    @deprecated(
      reason: "Please use `matchingProductClusters` field on `product` query instead."
    )
}

type CustomSetConnection {
  "The list of custom sets in the current page."
  edges: [CustomSetEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

type CustomSetEdge {
  "The custom set this edge refers to."
  node: CustomSet!
}

type CustomSet {
  "An optional description."
  description: String

  "The id of this specific product set."
  id: ID!

  "The list of images of the set. May be empty."
  images(first: Int): ImageConnection!

  "List of products in order of definition."
  products: [CustomSetProduct!]!

  "The list of tags of the set. May be empty."
  tags: [String!]!

  "An optional title."
  title: String
}

type ImageConnection {
  "The list of custom sets in the current page."
  edges: [ImageEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

type ImageEdge {
  "The custom set this edge refers to."
  node: Image!
}

type Image {
  "The url of the image"
  url: String!
}

type ProductReference {
  customSets(
    "An optional number of sets to fetch (default: 1)."
    first: Int
  ): CustomSetConnection

  matchingProductClusters(
    "An optional number of product clusters to fetch (default: 5)."
    first: Int
  ): ProductClusterConnection

  productSets(
    "An optional number of product sets to fetch (default: 10, maximum: 10)."
    first: Int
  ): ProductSetConnection

  similarProducts(
    "An optional number of products to fetch (default: 12)."
    first: Int
  ): ProductConnection
}

type ProductClusterConnection {
  "The list of clusters in the current page."
  edges: [ProductClusterEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

type ProductClusterEdge {
  "The cluster this edge refers to."
  node: ProductCluster!
}

type ProductCluster {
  "The id of this specific cluster."
  id: ID!

  """
  List of products contained in this cluster. Products are sorted by their
  likelihood of conversion.
  """
  products(
    "An optional number of products to fetch (default: 12)."
    first: Int
  ): ProductConnection!
}

interface IProduct {
  "The brand of this product."
  brand: String

  "The id of this product."
  id: ID!

  "The list of images of the product. May be empty."
  images: ImageConnection!

  "The model this product is based upon."
  modelId: String

  "The name of this product."
  name: String!

  "A list of tags attached to this product."
  tags: [String!]!

  "The online store URL of this product."
  url: String!

  "The list of variants for this product."
  variants(first: Int): ProductVariantConnection!
}

type ProductConnection {
  "The list of custom sets in the current page."
  edges: [ProductEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

type ProductEdge {
  "The custom set this edge refers to."
  node: Product!
}

type Product implements IProduct {
  "The brand of this product."
  brand: String

  "The id of this product."
  id: ID!

  "The list of images of the product. May be empty."
  images: ImageConnection!

  "The model this product is based upon."
  modelId: String

  "The name of this product."
  name: String!

  "A list of tags attached to this product."
  tags: [String!]!

  "The online store URL of this product."
  url: String!

  "The list of variants for this product."
  variants(first: Int): ProductVariantConnection!
}

type CustomSetProduct implements IProduct {
  "The brand of this product."
  brand: String

  "The id of this product."
  id: ID!

  "The list of images of the product. May be empty."
  images: ImageConnection!

  "The model this product is based upon."
  modelId: String

  "The name of this product."
  name: String!

  similarProducts(
    "An optional number of products to fetch (default: 12)."
    first: Int
  ): ProductConnection

  "A list of tags attached to this product."
  tags: [String!]!

  "The online store URL of this product."
  url: String!

  "The list of variants for this product."
  variants(first: Int): ProductVariantConnection!
}

"""
A "page" of product variants containing a given number of items.
"""
type ProductVariantConnection {
  "The list of product variants in the current page."
  edges: [ProductVariantEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

"""
An edge referring to a single product variant.
"""
type ProductVariantEdge {
  "The product variant this edge refers to."
  node: ProductVariant!
}

"""
The specific variant of a product.
"""
type ProductVariant {
  "The id referring to this specific product variant."
  id: ID!

  "The manufacturer's suggested retail price."
  manufacturerSuggestedRetailPrice: Price

  "The price of this product variant."
  price: Price!

  variantId: String
}

enum CurrencyCode {
  "Euro (EUR)"
  EUR

  "Polish Zlotych (PLN)"
  PLN

  "United States Dollars (USD)"
  USD
}

type Price {
  """
  The decimal amount of the price, e.g "19.99".
  """
  amount: String!

  "The currency of the price."
  currencyCode: CurrencyCode!
}

"""
The pagination information of the current page.
"""
type PageInfo {
  "The cursor identifying the end of the current page."
  endCursor: String

  "Indicates whether more data is available"
  hasNextPage: Boolean!
}

"""
@deprecated
The set of query types supported by `setCompose`.
"""
enum SetComposeQueryType {
  "The stock keeping unit identifier for a set of different product variants."
  MAIN_SKU
  "The identifier specifying a set of different products of the same model."
  MODEL_ID
  "The stock keeping unit identifier for a specific product."
  SKU
}

"""
@deprecated
The trigger product for which to query product sets.
"""
input SetComposeQueryInput {
  "The type of identifier by which to specify the trigger product."
  queryType: SetComposeQueryType!
  "The actual identifier to which the `queryType` refers."
  value: String!
}

"""
@deprecated
A "page" of product sets containg a given number of items.
"""
type SetComposeConnection {
  "The list of product sets in the current page."
  edges: [SetComposeEdge!]!
  "The pagination information of the current page."
  pageInfo: PageInfo
}

"""
@deprecated
An edge referring to a single product set.
"""
type SetComposeEdge {
  "The product set this edge refers to."
  node: SetCompose!
}

"""
@deprecated
A product set based on a given trigger product.
"""
type SetCompose {
  "An optional description of the product set."
  description: String
  "The id of this specific product set."
  id: String!
  "List of cross-selling products that combine well with the trigger product."
  setProducts: [SetComposeProduct!]!
  "The id of the snapshot for the specific product set used in this API request."
  snapshotId: String
  "An optional title of the product set."
  title: String
  """
  The trigger product this product set is based upon.
  _Note: If this product is currently unavailable the closest alternative will
  be used._
  """
  triggerProduct: SetComposeProduct!
}

"""
@deprecated
A specific product.
"""
type SetComposeProduct {
  """
  The list of similar products to potentially substitute this product with in
  a product set. Products are sorted by their likelihood of conversion.
  """
  alternatives(first: Int): SetComposeProductAlternativeConnection!
  "The model this product is based upon."
  model: ProductModel
  "The stock keeping unit of this product."
  sku: String!
  "The list of variants for this product."
  variants: SetComposeProductVariantConnection!
}

"""
@deprecated
A "page" of alternative products containing a given number of items.
"""
type SetComposeProductAlternativeConnection {
  "The list of similar products in the current page."
  edges: [SetComposeProductAlternativeEdge!]!
  "The pagination information of the current page."
  pageInfo: PageInfo
}

"""
@deprecated
An edge referring to a single alternative product.
"""
type SetComposeProductAlternativeEdge {
  "The alternative product this edge refers to."
  node: SetComposeProductAlternative!
}

"""
@deprecated
A specific alternative product.
"""
type SetComposeProductAlternative {
  "The model this product is based upon."
  model: ProductModel
  "The stock keeping unit of this product."
  sku: String!
  "The list of variants for this product."
  variants: SetComposeProductVariantConnection!
}

"""
@deprecated
A model uniting several products.
"""
type ProductModel {
  id: String!
}

"""
@deprecated
A "page" of product variants containing a given number of items.
"""
type SetComposeProductVariantConnection {
  "The list of product variants in the current page."
  edges: [SetComposeProductVariantEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

"""
@deprecated
An edge referring to a single product variant.
"""
type SetComposeProductVariantEdge {
  "The product variant this edge refers to."
  node: SetComposeProductVariant!
}

"""
@deprecated
The specific variant of a product.
"""
type SetComposeProductVariant {
  "The stock keeping unit referring to this specific product variant."
  sku: String!
}

type ProductSetConnection {
  "The list of product sets in the current page."
  edges: [ProductSetEdge!]!

  "The pagination information of the current page."
  pageInfo: PageInfo
}

type ProductSetEdge {
  "The product set this edge refers to."
  node: ProductSet!
}

type ProductSet {
  "The id of this specific product set."
  id: ID!

  "The image of the product set. May be null."
  imageLink: String

  "List of products in order of definition."
  products: [ProductSetProduct!]!

  "The list of tags of the product set. May be empty."
  tags: [String!]!
}

type ProductSetProduct {
  "The brand of this product."
  brand: String!

  "The id of this product."
  id: ID!

  "The image URL of the product."
  imageLink: String!

  "The online store URL of this product."
  link: String!

  """
  The decimal amount of the price including currency in ISO 4217, e.g "19.99 EUR".
  """
  price: String!

  """
  The decimal amount of sale the price including currency in ISO 4217, e.g "15.99 EUR".
  Discounted price if applicable, otherwise null. Same format as `price`.
  """
  salePrice: String

  "A list of tags attached to this product. May be empty."
  tags: [String!]!

  "The name of this product."
  title: String!
}

```

## Introspection Query

Through its introspection capabilities, GraphQL also allows to get information about the underlying schema using [**regular API queries**](https://docs.8select.io/api/general/introduction#how-to-query-graphql). The following example allows to fetch information about all top-level queries, their retrievable fields as well as parameters that can be passed to them:

```graphql
query {
  __schema {
    queryType {
      kind
      name
      fields {
        name
        args {
          name
          type {
            kind
            name
          }
        }
        type {
          fields {
            name
            type {
              kind
              name
            }
          }
        }
      }
    }
  }
}

```

Using appropriate tooling though, it will not be necessary to query this information most of the time. Instead almost every modern development environment has built-in functionalities to provide the information automatically.


# Examples


# 8.SIMILAR

Fetch similar products for a given product identifier.

## What we want to build

We want to show a slider with up to 8 similar products on our product page.

<figure><img src="/files/PpLmY2WwybCyuY1wMv38" alt=""><figcaption></figcaption></figure>

## GraphQL query and response - id only

To ensure that you show the most recent data in you shop you should only query the product ids and fetch the rest of the data you need for your UI from your own database.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    similarProducts(first: 8) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    similarProducts(first: 8) {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n\t}\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-7"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-8"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-2"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-3"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-4"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-5"
            }
          }
        ]
      }
    }
  }
}

```

{% endtab %}
{% endtabs %}

## GraphQL query and response - full data

In case you can not make the extra roundtrip you can actually query all the data that is required to render a basic UI.

{% tabs %}
{% tab title="GraphQL query" %}

````graphql
```groovy
query {
  product(id: "8S-DEMO-Polohemd-1") {
    similarProducts(first: 5) {
      edges {
        node {
          id
          brand
          images {
            edges {
              node {
                url
              }
            }
          }
          modelId
          name
          tags
          url          
          variants {
            edges {
              node {
                id
                variantId
                manufacturerSuggestedRetailPrice {
                  amount
                  currencyCode
                }
                price {
                  amount
                  currencyCode
                }
                
              }
            }
          }
        }
      }
    }
  }
}
```
````

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    similarProducts(first: 5) {\n      edges {\n        node {\n          id\n          brand\n          images {\n            edges {\n              node {\n                url\n              }\n            }\n          }\n          modelId\n          name\n          tags\n          url          \n          variants {\n            edges {\n              node {\n                id\n                variantId\n                manufacturerSuggestedRetailPrice {\n                  amount\n                  currencyCode\n                }\n                price {\n                  amount\n                  currencyCode\n                }\n                \n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10",
              "brand": "8select Fashion",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-10.jpg"
                    }
                  }
                ]
              },
              "modelId": "8S-DEMO-Polohemd",
              "name": "Polohemd Alternative 10",
              "tags": [
                "sale",
                "sale_discount_4"
              ],
              "url": "https://www.8select.com/",
              "variants": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-10-M",
                      "variantId": "M",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9",
              "brand": "8select Fashion",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-9.jpg"
                    }
                  }
                ]
              },
              "modelId": "8S-DEMO-Polohemd",
              "name": "Polohemd Alternative 9",
              "tags": [
                "sale",
                "sale_discount_4"
              ],
              "url": "https://www.8select.com/",
              "variants": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-9-M",
                      "variantId": "M",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-7",
              "brand": "8select Fashion",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-7.jpg"
                    }
                  }
                ]
              },
              "modelId": "8S-DEMO-Polohemd",
              "name": "Polohemd Alternative 7",
              "tags": [
                "sale",
                "sale_discount_4"
              ],
              "url": "https://www.8select.com/",
              "variants": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-7-M",
                      "variantId": "M",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-8",
              "brand": "8select Fashion",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-8.jpg"
                    }
                  }
                ]
              },
              "modelId": "8S-DEMO-Polohemd",
              "name": "Polohemd Alternative 8",
              "tags": [
                "sale",
                "sale_discount_4"
              ],
              "url": "https://www.8select.com/",
              "variants": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-8-M",
                      "variantId": "M",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-2",
              "brand": "8select Fashion",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd.png"
                    }
                  }
                ]
              },
              "modelId": "8S-DEMO-Polohemd",
              "name": "Polohemd Alternative 2",
              "tags": [
                "sale",
                "sale_discount_4"
              ],
              "url": "https://www.8select.com/",
              "variants": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-2-M",
                      "variantId": "M",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-2-L",
                      "variantId": "L",
                      "manufacturerSuggestedRetailPrice": {
                        "amount": "155.55",
                        "currencyCode": "EUR"
                      },
                      "price": {
                        "amount": "150.00",
                        "currencyCode": "EUR"
                      }
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

```

{% endtab %}
{% endtabs %}


# 8.SET (simplified)

Fetch cross selling products for a given product identifier. The response is already curated into readily usable product sets.

Revised version of getting cross selling products.

{% hint style="success" %}
We heard your feedback and combined 8.SET and 8.SET Custom into one query that already creates useable product sets. :tada:Your feedback is always welcome and helpful! :heart:
{% endhint %}

{% hint style="info" %}
The product sets do not have any `title` or `description` anymore but may have `tags`.

Also a product set will have either one or no `image` instead of a list of images.    &#x20;

We also work on automatically generating images for any product set, so you can present your products in the most optional way!
{% endhint %}

## GraphQL Query

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    productSets {
      edges {
        node {
          id
          tags
          imageLink
          products {
            id            
          }
        }
      }
    }
  }
}
```

## GraphQL Response

{% code expandable="true" %}

```json
{
  "data": {
    "product": {
      "productSets": {
        "edges": [
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
              "tags": ["everyday"],
              "imageLink": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg",
              "products": [
                {
                  "id": "8S-DEMO-Jacket-1"
                },
                {
                  "id": "8S-DEMO-Polohemd-1"
                },
                {
                  "id": "8S-DEMO-Gürtel-1"
                },
                {
                  "id": "8S-DEMO-Hose-1"
                },
                {
                  "id": "8S-DEMO-Schuhe-1"
                }
              ]
            }
          },
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1",
              "tags": [],
              "imageLink": null,
              "products": [
                {
                  "id": "8S-DEMO-Jacket-2"
                },
                {
                  "id": "8S-DEMO-Polohemd-2"
                },
                {
                  "id": "8S-DEMO-Gürtel-2"
                },
                {
                  "id": "8S-DEMO-Hose-2"
                },
                {
                  "id": "8S-DEMO-Schuhe-2"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endcode %}


# 8.SET

Fetch cross selling products for a given product identifier.

## What we want to build

We want to show a slider with cross selling content. We want to limit the number of different product categories we show to 5 and only want 3 products per category. We will then end up with up to 15 matching products.

{% hint style="info" %}
The response will not include the product the content was requested for. If you want to include it in your UI you need to add it to the list of products on your side.
{% endhint %}

<figure><img src="/files/utLSOVhn9kuyAhNXsPiK" alt=""><figcaption></figcaption></figure>

We will request 5 matching clusters with up to 3 products each. We actually get back less products in our example - 3 shirts; 2 watches; 3 girdles; 2 trousers; 3 gloves

Our **UI will be rendered by alternating over the 5 clusters**, i.e. we take one product out of each cluster and then start the iteration again until no more products are left.

{% hint style="warning" %}
We strongly encourage you to use the new [`productSets()`](/api/examples/8.set-simplified) query!
{% endhint %}

## GraphQL query and response - id only

To ensure that you show the most recent data in you shop you should only query the product ids and fetch the rest of the data you need for your UI from your own database.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    matchingProductClusters(first: 5) {
      edges {
        node {
          products(first: 3) {
            edges {
              node {
                id
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    matchingProductClusters(first: 5) {\n      edges {\n        node {\n          products(first: 3) {\n            edges {\n              node {\n                id\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-10"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-7"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-8"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-2"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-3"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-4"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-1"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-3"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-2"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-3"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

## GraphQL query and response - full data

In case you can not make the extra roundtrip you can actually query all the data that is required to render a basic UI.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    matchingProductClusters(first: 6) {
      edges {
        node {
          products(first: 3) {
            edges {
              node {
                id
                brand
                images {
                  edges {
                    node {
                      url
                    }
                  }
                }
                modelId
                name
                tags
                url
                variants {
                  edges {
                    node {
                      id
                      variantId
                      manufacturerSuggestedRetailPrice {
                        amount
                        currencyCode
                      }
                      price {
                        amount
                        currencyCode
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    matchingProductClusters(first: 6) {\n      edges {\n        node {\n          products(first: 3) {\n            edges {\n              node {\n                id\n                brand\n                images {\n                  edges {\n                    node {\n                      url\n                    }\n                  }\n                }\n                modelId\n                name\n                tags\n                url\n                variants {\n                  edges {\n                    node {\n                      id\n                      variantId\n                      manufacturerSuggestedRetailPrice {\n                        amount\n                        currencyCode\n                      }\n                      price {\n                        amount\n                        currencyCode\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-10",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-10.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Polohemd",
                      "name": "Polohemd Alternative 10",
                      "tags": [
                        "sale",
                        "sale_discount_4"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Polohemd-10-M",
                              "variantId": "M",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "155.55",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "150.00",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-7",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-7.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Polohemd",
                      "name": "Polohemd Alternative 7",
                      "tags": [
                        "sale",
                        "sale_discount_4"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Polohemd-7-M",
                              "variantId": "M",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "155.55",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "150.00",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-8",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Polohemd-8.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Polohemd",
                      "name": "Polohemd Alternative 8",
                      "tags": [
                        "sale",
                        "sale_discount_4"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Polohemd-8-M",
                              "variantId": "M",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "155.55",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "150.00",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-1",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Uhr.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Uhr",
                      "name": "Uhr",
                      "tags": [],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Uhr-1",
                              "variantId": "onesize",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "149.49",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "149.49",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-2",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Uhr-2.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Uhr",
                      "name": "Uhr Alternative 2",
                      "tags": [],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Uhr-2",
                              "variantId": "onesize",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "149.49",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "149.49",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-3",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Guertel-3.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Gürtel",
                      "name": "Gürtel Alternative 3",
                      "tags": [
                        "sale",
                        "sale_discount_7"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-3-95",
                              "variantId": "95",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-3-100",
                              "variantId": "100",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-3-105",
                              "variantId": "105",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-3-110",
                              "variantId": "110",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-3-120",
                              "variantId": "120",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-4",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Guertel-4.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Gürtel",
                      "name": "Gürtel Alternative 4",
                      "tags": [
                        "sale",
                        "sale_discount_7"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-4-95",
                              "variantId": "95",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-4-100",
                              "variantId": "100",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-4-105",
                              "variantId": "105",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-4-110",
                              "variantId": "110",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-4-120",
                              "variantId": "120",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-1",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Guertel.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Gürtel",
                      "name": "Gürtel",
                      "tags": [
                        "sale",
                        "sale_discount_7"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-1-95",
                              "variantId": "95",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-1-100",
                              "variantId": "100",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-1-105",
                              "variantId": "105",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-1-110",
                              "variantId": "110",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Gürtel-1-120",
                              "variantId": "120",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "35.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "33.33",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-1",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Hose.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Hose",
                      "name": "Hose",
                      "tags": [],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-1-32-32",
                              "variantId": "32-32",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-1-32-34",
                              "variantId": "32-34",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-1-34-34",
                              "variantId": "34-34",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-1-36-32",
                              "variantId": "36-32",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-3",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Hose-3.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Hose",
                      "name": "Hose Alternative 3",
                      "tags": [],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-3-32-32",
                              "variantId": "32-32",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-3-32-34",
                              "variantId": "32-34",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-3-34-34",
                              "variantId": "34-34",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Hose-3-36-32",
                              "variantId": "36-32",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "79.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-1",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Handschuhe.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Handschuhe",
                      "name": "Handschuhe",
                      "tags": [
                        "sale",
                        "sale_discount_20"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-1-8",
                              "variantId": "8",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "49.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-1-9",
                              "variantId": "9",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "49.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-1-10",
                              "variantId": "10",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "49.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-2",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Handschuhe-2.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Handschuhe",
                      "name": "Handschuhe Alternative 2",
                      "tags": [
                        "sale",
                        "sale_discount_13"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-2-8",
                              "variantId": "8",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "45.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-2-9",
                              "variantId": "9",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "45.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-2-10",
                              "variantId": "10",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "43.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-3",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Handschuhe-3.jpg"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Handschuhe",
                      "name": "Handschuhe Alternative 3",
                      "tags": [
                        "sale",
                        "sale_discount_33"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-3-8",
                              "variantId": "8",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "29.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "29.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-3-9",
                              "variantId": "9",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Handschuhe-3-10",
                              "variantId": "10",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "59.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "39.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Jacket-2",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Jacket.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Jacket",
                      "name": "Jacket Alternative",
                      "tags": [
                        "sale",
                        "sale_discount_20"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Jacket-2-48",
                              "variantId": "48",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "199.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "159.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Jacket-1",
                      "brand": "8select Fashion",
                      "images": {
                        "edges": [
                          {
                            "node": {
                              "url": "https://wgt-prod.staging.8select.io/db54750f-80fc-4818-9455-30ca233225dc/demo/images/Jacket.png"
                            }
                          }
                        ]
                      },
                      "modelId": "8S-DEMO-Jacket",
                      "name": "Jacket",
                      "tags": [
                        "sale",
                        "sale_discount_10"
                      ],
                      "url": "https://www.8select.com/",
                      "variants": {
                        "edges": [
                          {
                            "node": {
                              "id": "8S-DEMO-Jacket-1-48",
                              "variantId": "48",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Jacket-1-52",
                              "variantId": "52",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Jacket-1-54",
                              "variantId": "54",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              }
                            }
                          },
                          {
                            "node": {
                              "id": "8S-DEMO-Jacket-1-98",
                              "variantId": "98",
                              "manufacturerSuggestedRetailPrice": {
                                "amount": "99.99",
                                "currencyCode": "EUR"
                              },
                              "price": {
                                "amount": "89.99",
                                "currencyCode": "EUR"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

```

{% endtab %}
{% endtabs %}

a


# 8.SET Custom

Fetch a custom set for a given product identifier or a specific custom set by id.

## What we want to build

We want to show the set images and a slider with the set products. We only want to show up to 2 similar products for each product.

<figure><img src="/files/2yh7DMOJVEeFqNoWDBE6" alt=""><figcaption></figcaption></figure>

## GraphQL query and response

You can request custom set content for a given product identifier or you can request a specific set by it's id.

### Custom Set for product - id only

To ensure that you show the most recent data in you shop you should only query the product ids and fetch the rest of the data you need for your UI from your own database.

In case you can not make the extra roundtrip you can actually query all the data that is required to render a basic UI. Just consult the GraphQL schema.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    customSets {
      edges {
        node {
          id
          title
          description
          images {
            edges {
              node {
                url
              }
            }
          }
          products {
            id
            similarProducts(first: 2) {
              edges {
                node {
                  id
                }
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    customSets {\n      edges {\n        node {\n          id\n          title\n          description\n          images {\n            edges {\n              node {\n                url\n              }\n            }\n          }\n          products {\n            id\n            similarProducts(first: 2) {\n              edges {\n                node {\n                  id\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "customSets": {
        "edges": [
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
              "title": "8SELECT Fashion for everyday",
              "description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg"
                    }
                  }
                ]
              },
              "products": [
                {
                  "id": "8S-DEMO-Jacket-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Jacket-2"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Polohemd-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Polohemd-10"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Polohemd-9"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Gürtel-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Gürtel-3"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Gürtel-4"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Hose-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Hose-2"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Hose-3"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Schuhe-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Schuhe-2"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Schuhe-3"
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Custom Set by id - id only

To ensure that you show the most recent data in you shop you should only query the product ids and fetch the rest of the data you need for your UI from your own database.

In case you can not make the extra roundtrip you can actually query all the data that is required to render a basic UI. Just consult the GraphQL schema.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  customSet(id: "e3bcaf66-5037-4d7b-be4a-8c571a6fb299") {
    id
    title
    description
    images {
      edges {
        node {
          url
        }
      }
    }
    products {
      id
      similarProducts(first: 2) {
        edges {
          node {
            id
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  customSet(id: \"e3bcaf66-5037-4d7b-be4a-8c571a6fb299\") {\n    id\n    title\n    description\n    images {\n      edges {\n        node {\n          url\n        }\n      }\n    }\n    products {\n      id\n      similarProducts(first: 2) {\n        edges {\n          node {\n            id\n          }\n        }\n      }\n    }\n  }\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "customSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
      "title": "8SELECT Fashion for everyday",
      "description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
      "images": {
        "edges": [
          {
            "node": {
              "url": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg"
            }
          }
        ]
      },
      "products": [
        {
          "id": "8S-DEMO-Jacket-1",
          "similarProducts": {
            "edges": [
              {
                "node": {
                  "id": "8S-DEMO-Jacket-2"
                }
              }
            ]
          }
        },
        {
          "id": "8S-DEMO-Polohemd-1",
          "similarProducts": {
            "edges": [
              {
                "node": {
                  "id": "8S-DEMO-Polohemd-10"
                }
              },
              {
                "node": {
                  "id": "8S-DEMO-Polohemd-9"
                }
              }
            ]
          }
        },
        {
          "id": "8S-DEMO-Gürtel-1",
          "similarProducts": {
            "edges": [
              {
                "node": {
                  "id": "8S-DEMO-Gürtel-3"
                }
              },
              {
                "node": {
                  "id": "8S-DEMO-Gürtel-4"
                }
              }
            ]
          }
        },
        {
          "id": "8S-DEMO-Hose-1",
          "similarProducts": {
            "edges": [
              {
                "node": {
                  "id": "8S-DEMO-Hose-2"
                }
              },
              {
                "node": {
                  "id": "8S-DEMO-Hose-3"
                }
              }
            ]
          }
        },
        {
          "id": "8S-DEMO-Schuhe-1",
          "similarProducts": {
            "edges": [
              {
                "node": {
                  "id": "8S-DEMO-Schuhe-2"
                }
              },
              {
                "node": {
                  "id": "8S-DEMO-Schuhe-3"
                }
              }
            ]
          }
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Product Page - All Content

Fetch all available content for a given product identifier.

## What we want to build

We want to show a custom set, similar products and matching products on our product page.

With the power of GraphQL we can request not only one type of content but all in one query. That way we can fetch all available content with just one HTTP request.

For the matching products we do not want to show similar products for our current product, only other matching categories. So we will request up to 6 `matchingProductClusters` and will skip the first one when we render our UI.

<figure><img src="/files/zR6h9344UMQUnwh1pKHy" alt=""><figcaption></figcaption></figure>

## GraphQL query and response - id only

To ensure that you show the most recent data in you shop you should only query the product ids and fetch the rest of the data you need for your UI from your own database.

In case you can not make the extra roundtrip you can actually query all the data that is required to render a basic UI. Just consult the GraphQL schema.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    matchingProductClusters(first: 6) {
      edges {
        node {
          products(first: 3) {
            edges {
              node {
                id
              }
            }
          }
        }
      }
    }
    similarProducts(first: 8) {
      edges {
        node {
          id
        }
      }
    }
    customSets {
      edges {
        node {
          id
          title
          description
          images {
            edges {
              node {
                url
              }
            }
          }
          products {
            id
            similarProducts(first: 2) {
              edges {
                node {
                  id
                }
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="cURL request" %}
{% code overflow="wrap" %}

```bash
curl --request POST \
  --url https://api.8select.io/graphql \
  --header 'Content-Type: application/json' \
  --header 'x-api-id: db54750f-80fc-4818-9455-30ca233225dc' \
  --data '{"query":"query {\n  product(id: \"8S-DEMO-Polohemd-1\") {\n    matchingProductClusters(first: 6) {\n      edges {\n        node {\n          products(first: 3) {\n            edges {\n              node {\n                id\n              }\n            }\n          }\n        }\n      }\n    }\n    similarProducts(first: 8) {\n      edges {\n        node {\n          id\n        }\n      }\n    }\n    customSets {\n      edges {\n        node {\n          id\n          title\n          description\n          images {\n            edges {\n              node {\n                url\n              }\n            }\n          }\n          products {\n            id\n            similarProducts(first: 2) {\n              edges {\n                node {\n                  id\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"}'
```

{% endcode %}
{% endtab %}

{% tab title="response" %}

```json
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-10"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-7"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Polohemd-8"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Uhr-2"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-3"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-4"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Gürtel-1"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Hose-3"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-1"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-2"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Handschuhe-3"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "8S-DEMO-Jacket-2"
                    }
                  },
                  {
                    "node": {
                      "id": "8S-DEMO-Jacket-1"
                    }
                  }
                ]
              }
            }
          }
        ]
      },
      "similarProducts": {
        "edges": [
          {
            "node": {
              "id": "8S-DEMO-Polohemd-1"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-10"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-9"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-7"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-8"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-2"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-3"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-4"
            }
          },
          {
            "node": {
              "id": "8S-DEMO-Polohemd-5"
            }
          }
        ]
      },
      "customSets": {
        "edges": [
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
              "title": "8SELECT Fashion for everyday",
              "description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
              "images": {
                "edges": [
                  {
                    "node": {
                      "url": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg"
                    }
                  }
                ]
              },
              "products": [
                {
                  "id": "8S-DEMO-Jacket-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Jacket-2"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Polohemd-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Polohemd-10"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Polohemd-9"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Gürtel-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Gürtel-3"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Gürtel-4"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Hose-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Hose-2"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Hose-3"
                        }
                      }
                    ]
                  }
                },
                {
                  "id": "8S-DEMO-Schuhe-1",
                  "similarProducts": {
                    "edges": [
                      {
                        "node": {
                          "id": "8S-DEMO-Schuhe-2"
                        }
                      },
                      {
                        "node": {
                          "id": "8S-DEMO-Schuhe-3"
                        }
                      }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Changelog

## 1.3.0 - 2024-07-25

### Implemented Features

* Support new content types according to [GraphQL API 2.0.0](/api/changelog/2.0.0)

## 1.0.0 - 2021-07-08

### Implemented Features

* Initial release


# General

Tracking for 8.API

In order to enable insights about user behaviour with our content you have to send some analytics events. For our ready to use widgets this is done automatically. If you use 8.API and create your own UI those events have to be sent by your UI.

{% hint style="warning" %}
Implementing this tracking is only necessary if you use 8.API and create your own UIs to present our content.

If you use widgets you do not need this!
{% endhint %}


# Introduction

## How to send events?

The analytics API is built using REST. To submit an event you send a POST request to `https://api.8select.io/analytics/v3/events`:

```bash
POST /analytics/v3/events
Host: https://api.8select.io
Content-Type: application/json
x-api-id: <Your API ID>

{
  "type": "view",
  "view": {
    "type": "customSet",
    "customSet": {
      "id": "1b3de0bd-95c6-435a-8bb9-f4cae0160388"
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

Every event has to contain a `type` and a type-specific payload, as well as an optional `context` array.

### context

The context is used to provide additional contextual information about the origin of a given event. You can find out more in the [context](/api-tracking/general/context) section.

#### context\[type = "user"].user.id

The `id` sent in the user context within the context array must be consistent for all events concerning a single user. It should be anonymized, i.e. a generated id without any connection to personal data. You can read the section about [user identification](/api-tracking/general/user-identification) to learn more.

### type

There are currently three types of events (view, interact, order), each with a specific payload as defined in the respective sections under [events](/api-tracking/events).\
The property containing the type specific payload is always named after the type itself.


# Authentication

To obtain your API ID for use with the 8SELECT API Tracking, please speak to your account manager.‌

Your API ID is permanently linked to your 8SELECT account. It must be included in every call to the tracking API as a `x-api-id` header to authorize your request:

```bash
POST /analytics/v3/events
Host: https://api.8select.io
x-api-id: <Your API ID>
```


# Context

As implied by its name the context is supposed to provide contextual information about an event. This will help to classify and interpret KPIs generated from the events. Put differently the context describes the user journey from the inside out. This can range from the specifics of single elements in a product set to global information such as the containing page or even enduser navigating the pages. Some of this contextual data will be attached at the API level. Other information has to be supplied from the client-side.

```javascript
{
  ...,
  "context": [
    {
      "content": {
        "customSet": {
          "id": "1b3de0bd-95c6-435a-8bb9-f4cae0160388",
        },
        "type": "customSet"
      },
      "type": "content",
    }
  ]
}
```

Similarly to the event structure itself, `context` elements will always have a `type` and optionally a type-specific payload named thereafter.&#x20;

{% hint style="info" %}
Currently, only two context types are supported: `content` and `user`.
{% endhint %}

## user

{% hint style="danger" %}
Currently **user-based metrics are required** for our current pricing mode&#x6C;**,** i.e. you have to provide a `user` context.
{% endhint %}

The `user` context is responsible for the event attribution:

```javascript
{ 
  "type": "user",
  "user": {
    "id": "<user-id>"
  }
}
```

Read the section about [user identification](/api-tracking/general/user-identification) for more information on user-based metrics and the requirements for the anonymized user ID.

## content

If the tracked interaction takes place within the context of some 8SELECT-provided content, e.g. a click on one of the products in an 8.SET-Custom product set, the following information must be included in the context of this event:

```javascript
{
  "content": {
    "customSet": {
      "id": "<set-id>",
    },
    "type": "customSet"
  },
  "type": "content",
}
```


# User Identification

To enable user-based metrics the user has to be identified with a consistent identifier. The identifier is wrapped in a context object and must be consistent for all events concerning a single user:

```javascript
{
  ...,
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67" // uuid-v4
      }
    }
  ],	
  ...
}
```

## Getting a user-id from us

We provide a HTTP endpoint you can use to request a user-id. The endpoint will return a JSON payload that includes a uuid-v4.

HTTP GET`https://api.8select.io/analytics/v3/user`

```json
{
  "id": "c243ddce-e122-4532-88bb-460597632640"
}
```

{% hint style="danger" %}
This will only work if you make the request from the client side - i.e. you have to implement this in your frontend and the request has to be send via the user's browser.
{% endhint %}

The ID generation is e-tag based and will only work on the client side. If the browser cache is emptied we will return a new id.

<figure><img src="/files/XaLgXJdPFs04lK4fNj7F" alt=""><figcaption></figcaption></figure>

## Generating your own user-id

In case you want or can generate your own user-id or maybe already have some means to identify a user that is also fine. Just make sure you send us a uuid-v4. In case you already have a value that is not a uuid-v4 you can transform your value as a [uuid-v5](https://www.npmjs.com/package/uuidv5).

Identifying the user has to be done on a best efforts basis. We know that making this work 100% of the time is impossible because users can clear their device cache or change devices or something else to break the consistency.

{% hint style="info" %}
**User-based metrics** are metrics that actually require to tie an event stream to a certain user.&#x20;

One example is the metric about order lines that are generated through 8SELECT content.&#x20;

To know that specific products where bought because a user interacted with that product via the 8SELECT content all those events need to be tied to that specific user, i.e. need a user id in the context. Otherwise it is not possible to match order events with view or interact events.
{% endhint %}


# Event Validation

Incoming analytics events are validated before they are accepted and processed. If you encounter any errors during the integration of 8.API Tracking in your product, you can check the structure of your events against our validation test endpoint at `https://api.8select.io/analytics/v3/events/validate`:

```bash
POST /analytics/v3/events/validate
Host: https://api.8select.io
Content-Type: application/json
x-api-id: <Your API ID>

{
  "type": "view",
  "view": {
    "type": "customSet"
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The above event is missing the `customSet` property, which is required for `view` events of type `"customSet"`. The validation endpoint will thus among others report this violation:

```javascript
[
  ...,
  {
    "instancePath": "/view",
    "schemaPath": "#/definitions/PayloadCustomSet/required",
    "keyword": "required",
    "params": {
      "missingProperty": "customSet"
    },
    "message": "must have required property 'customSet'"
  },
  ...
]
```

{% hint style="warning" %}
Please note, that the validation endpoint is currently set to be very verbose. This means, it will often contain multiple alerts caused by the same schema violation. We are working on a solution to make the output more concise.
{% endhint %}


# Events

For the time being we only support a limited amount of KPIs and therefor only a limited amount of events is required if you want to use those KPIs.

Currently there are three types of events: `view`, `interact` and `order`.

### [view](/api-tracking/events/view)

Whenever the user has seen some 8SELECT content, i.e. it was in the device's viewport. For example if an 8.SET Compose content is presented to the user.

### [interact](/api-tracking/events/interact)

Whenever the user has interacted with 8SELECT content. For example clicked on a product.

Interact events and order events from users are matched and are the base of the commission based pricing. If user A interacts with content from 8.API, e.g. product 42 and later buys product 42, 8SELECT will get a commission.

### [order](/api-tracking/events/order)

Whenever the user makes an order.


# view

A view event should be triggered whenever [**a piece of content slides into the users viewport**](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent). Assuming that you are showing 8.SET-Custom product set data, that event should look like the following example:

```javascript
{
  "type": "view",
  "view": {
    "type": "customSet",
    "customSet": {
      "id": "1b3de0bd-95c6-435a-8bb9-f4cae0160388"
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

A view event is identified by its type `view` and an additional `view` property containing specific information about the content being viewed. This property should in turn have a type and content-specific payload.

## 8.SIMILAR - similarProducts

A view event with type `similarProducts` must contain a `similarProducts` property containing the `id` of the product the content was requested for.

In our example we request content for `8S-DEMO-Polohemd-1` and also use that as the id in the tracking event.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    similarProducts(first: 8) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="GraphQL response" %}

```json
{
  "data": {
    "product": {
      "similarProducts": {
        "edges": [
          ...
        ]
      }
    }
  }
}

```

{% endtab %}

{% tab title="Event payload to send" %}

```json
{ 
  ...,
  "view": {
    "type": "similarProducts",
    "similarProducts": {
      "id": "8S-DEMO-Polohemd-1",
    }
  },
  ...
}
```

{% endtab %}
{% endtabs %}

## 8.SET - matchingProductClusters

A view event with type `matchingProductClusters` must contain a `matchingProductClusters` property containing the `id` of the product the content was requested for.

In our example we request content for `8S-DEMO-Polohemd-1` and also use that as the id in the tracking event.

{% tabs %}
{% tab title="GraphQL query" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    matchingProductClusters(first: 5) {
      edges {
        node {
          products(first: 3) {
            edges {
              node {
                id
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="GraphQL response" %}

```json
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              "products": {
                "edges": [
                  ...
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  ...
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  ...
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  ...
                ]
              }
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  ...
                ]
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}

{% tab title="Event payload to send" %}

```json
{ 
  ...,
  "view": {
    "type": "matchingProductClusters",
    "matchingProductClusters": {
      "id": "8S-DEMO-Polohemd-1",
    }
  },
  ...
}
```

{% endtab %}
{% endtabs %}

## 8.SET Custom - customSet

A view event with type `customSet` must contain a `customSet` property containing the `id` of the viewed product set.

The GraphQL API response includes the id of the set depending on the query you use:

* `data.product.customSets.edges[].node.id`
* `data.customSet.id`

{% tabs %}
{% tab title="GraphQL response (product)" %}

```graphql
{
  "data": {
    "product": {
      "customSets": {
        "edges": [
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
              "title": "8SELECT Fashion for everyday",
              "description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
              "images": {
                "edges": [
                  ...
                ]
              },
              "products": [
                ...
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}

{% tab title="GraphQL response (customSet)" %}

```graphql
{
  "data": {
    "customSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
      "title": "8SELECT Fashion for everyday",
      "description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
      "images": {
        "edges": [
          ...
        ]
      },
      "products": [
        ...
      ]
    }
  }
}
```

{% endtab %}

{% tab title="Event payload to send" %}

```json
{ 
  ...,
  "view": {
    "type": "customSet",
    "customSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
    }
  },
  ...
}
```

{% endtab %}
{% endtabs %}


# How to evaluate if view event can be sent

Our KPIs require that a `view` event is only send if the according content is inside the user's viewport.

## Example cases

There are two cases when content is eligible to trigger such an event

### **50% of the content is visible in the viewport**

![event is triggered when 50% of the content is visible](/files/-MfiezeT91tWepS20X-5)

### **The content is visible on 50% of the viewport**

![event is triggered when 50% of the viewport is filled with the content](/files/-Mfif4Q-tpOk4cVaC7H1)

## **Code Snippet**

```javascript
const getDimensions = node => {
  const nodeOffsetY = node.getBoundingClientRect().top + window.scrollY
  const nodeHeight = node.getBoundingClientRect().height
  const viewOffsetY = window.scrollY
  const viewHeight = window.innerHeight
  return { nodeOffsetY, nodeHeight, viewOffsetY, viewHeight }
}

const isHalfViewFilledByNode = node => {
  const { nodeOffsetY, nodeHeight, viewOffsetY, viewHeight } = getDimensions(node)
  const topNodeIsFillingBottomHalfView = viewOffsetY + viewHeight / 2 >= nodeOffsetY
  const bottomNodeIsFillingTopHalfView = viewOffsetY + viewHeight / 2 <= nodeOffsetY + nodeHeight

  return topNodeIsFillingBottomHalfView && bottomNodeIsFillingTopHalfView
}

const isHalfNodeVisible = node => {
  const { nodeOffsetY, nodeHeight, viewOffsetY, viewHeight } = getDimensions(node)
  const viewBottomIsBelowTopHalfNode = viewOffsetY + viewHeight >= nodeOffsetY + nodeHeight / 2
  const viewTopIsAboveBottomHalfNode = viewOffsetY <= nodeOffsetY + nodeHeight / 2

  return viewBottomIsBelowTopHalfNode && viewTopIsAboveBottomHalfNode
}

export default node => {
  const isNodeHidden = node.offsetParent === null // if one of the parents is display none, this will be null
  const isNodeFixed = node.style.position === 'fixed' // position fixed has no offsetParent

  if (isNodeHidden && !isNodeFixed) {
    return false
  }

  return isHalfViewFilledByNode(node) || isHalfNodeVisible(node)
}
```


# User views 8.SET content

Let's assume you are showing content from 8.SET that was loaded via [8.API](/api/examples/8.set). Whenever a user visits that page and sees that content you should send the respective `view` event. **Please ensure this event to be triggered only once per page view for a given set and only** [**once the content is at least 50% in the device's viewport or 50% of the content are in the viewport**](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent)**.**&#x20;

![](/files/-McFYIXOaj-TXoyaNh8N)

Suppose you requested content for product with SKU `457297-0001-00340`the event you send would look like this:

```javascript
{
  "type": "view",
  "view": {
    "type": "matchingProductClusters",
    "matchingProductClusters": {
      "id": "457297-0001-00340",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The type of the event is `view`. The corresponding `view` property is intended to specify the target of the event. In our case, this would be `matchingProductClusters` as that is the type of data we requested from the API. Again, to describe the target more specifically, we include the `id` of the product we requested content for.


# interact

An interact event should be triggered whenever a piece of content is interacted with by a user. Assuming that you are showing 8.SET product set data and a user clicks on a product contained in that set, the event should look like the following example:

```javascript
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "654321-7890"
    }
  },
  "context": [
    {
      "content": {
        "matchingProductClusters": {
          "id": "457297-0001-00340"
        },
        "type": "matchingProductClusters"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The context array must currently always contain an object with type `user` as defined in the [context](/api-tracking/general/context) section and the user ID specified therein must be a consistent identifier as described in the [user identification](/api-tracking/general/user-identification) section.&#x20;

An interact event is identified by its type `interact` and an additional `interact` property containing specific information about the content being interacted with. This property should in turn have a type and content-specific payload, as well as an optional third property `action` specifying the user interaction, e.g. `click` or `addToCart`.

{% hint style="info" %}
Currently, only `product` interactions are taken into consideration.
{% endhint %}

## product

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Some example actions: `click`, `addToCart`, `navigateTo`, `addToWishlist`, `like`, `share`.

```javascript
{ 
  ...,
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "654321-7890"
    }
  },
  ...
}
```

While an arbitrary `action` can be supplied we currently only have KPIs that differentiate between `addToCart` and any other action.&#x20;

{% hint style="info" %}
If you are not sure how to name the `action` simply omit the property.
{% endhint %}


# User clicks on a product within 8.SET content

Let's assume you are showing content from 8.SET that was loaded via [8.API](/api/examples/8.set). Whenever a user clicks on a product within that content you should send the respective `interact` event.

![](/files/-McFYMoWpHFSbVeWFwiM)

Suppose you requested content for product with SKU `457297-0001-00340`the API would return this:

```javascript
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              ...
            }
          },
          {
            "node": {
              ...
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "724502-0002"
                    }
                  },
                  ...
                ]
              }
            }
          },
          ...
        ]
      }
    }
  }
}
```

The user clicks on the first product of the third cluster.

The event you send would look like that:

```javascript
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "379217-0006"
    }
  },
  "context": [
    {
      "content": {
        "matchingProductClusters": {
          "id": "457297-0001-00340"
        },
        "type": "matchingProductClusters"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The type of the event is `interact`. The corresponding `interact` property is intended to specify the target and action of the event. In our case, this would be a `click` on a  `product`. Again, to describe the target more specifically, we include the `id` of the product interacted with — as returned in the API response.

Lastly, the `context` must contain an object with type `user`, as well as the `content` context specifying the type of content and the id as described in the [context](/api-tracking/general/context) section.


# User adds a product to their card from within 8.SET content

Let's assume you are showing content from 8.SET that was loaded via [8.API](/api/examples/8.set). Whenever a user adds a product from within that content to their cart you should send the respective `interact` event.

![](/files/-McFYLCy8N1aALZ76ets)

Suppose you requested content for product with SKU `457297-0001-00340`the API would return this:

```javascript
{
  "data": {
    "product": {
      "matchingProductClusters": {
        "edges": [
          {
            "node": {
              ...
            }
          },
          {
            "node": {
              "products": {
                "edges": [
                  {
                    "node": {
                      "id": "379217-0006"
                    }
                  },
                  ...
                ]
              }
            }
          },
          ...
        ]
      }
    }
  }
}
```

The event you send would look like that:

```javascript
{
  "type": "interact",
  "interact": {
    "action": "addToCart",
    "type": "product",
    "product": {
      "sku": "379217-0006"
    }
  },
  "context": [
    {
      "content": {
        "matchingProductClusters": {
          "id": "457297-0001-00340"
        },
        "type": "matchingProductClusters"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The type of the event is `interact`. The corresponding `interact` property is intended to specify the action and target of the event. In our case, this would be `addToCart` and  `product`. Again, to describe the target more specifically, we include the `id` of the product interacted with — as returned in the API response.

Lastly, the `context` must contain an object with type `user`, as well as the `content` context specifying the type of content and the id as described in the [context](/api-tracking/general/context) section.


# Example what is not a product interaction

Because our `interact` events are the reference events for our commission based pricing we want to make sure you only send interact events if the user is really interacting with the product and not with something else.

## Product Carousel

Let's assume you have implemented a product carousel where a user can click or swipe through a list of products. In that case a click or swipe to the next or previous product is definitely not an interact product event.

![this action must not trigger an interact product event](/files/-McLX1rCSlU9QBZMAqog)


# order

An order event should be triggered whenever a user, i.e. one of your customers, places an order. It should look like the following example.

{% hint style="warning" %}
Make sure you send the amount that 1 item costs as `amount` and not the sum of the order line.
{% endhint %}

```javascript
{
  "type": "order",
  "order": {
    "id": "0987654321", // either your original id or a pseudonymized value - i.e. same input always produces same anonymous output
    "lines": [
      {
        "sku": "654321-7890",
        "grossPrice": {
          "amount": 0.99, // unit price - i.e. price of 1 item not the sum of 5
          "currency": "EUR" // currency code as defined by ISO 4217
        },
        "quantity": 5 // how many items where bought
      },
      {
        "sku": "567890-4321",
        "grossPrice": {
          "amount": 49.95,
          "currency": "EUR"
        },
        "quantity": 1
      }
    ]
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

The context array must currently always contain an object with type `user` as defined in the [context](/api-tracking/general/context) section and the user ID specified therein must be a consistent identifier as described in the [user identification](/api-tracking/general/user-identification) section.&#x20;

An order event is identified by its type `order` and an additional `order` property containing specific information about the products being ordered. This property must have the `id` of the order in your shop system and a `lines` property, containing a list of ordered items.

Each order lines entry must contain the `sku` of the ordered product and the quantity of items ordered of this product. Additionally, it must include a `grossPrice` object with a `currency` code, as defined by [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217), and the `amount` of money paid (including e.g. VAT) for **one unit** of this entry, i.e. the price of a single item.

Lastly, the `context` must contain an object with type `user` as described in the [context](/api-tracking/general/context) section.

{% hint style="info" %}
Duplicate `order` events will be deduplicated using the `order.id` - you do not have to implement anything to prevent sending the order tracking event of an order multiple times.

But you need to make sure, that the `order.id` is always the same for the same order - i.e. use a determinstic approach to anonymize the `id` in case you do not send the original value.

Best would be to use an HMAC hash - for example SHA-256.
{% endhint %}

### example for order.id pseudonymization

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

```javascript
const crypto = require('crypto');

/**
 * Anonymizes an Order ID deterministically.
 * @param {string} orderId - The original order ID.
 * @param {string} secretKey - A private key known only to your system.
 * @returns {string} - A consistent hex-encoded hash.
 */
function anonymizeOrderId(orderId, secretKey) {
  return crypto
    .createHmac('sha256', secretKey)
    .update(orderId)
    .digest('hex');
}

// Example Usage:
const mySecret = 'super-secret-pepper-key-123';
const originalId = 'ORD-99502';

const anonymizedId1 = anonymizeOrderId(originalId, mySecret);
const anonymizedId2 = anonymizeOrderId(originalId, mySecret);

console.log(`Original: ${originalId}`);
console.log(`Anonymized 1: ${anonymizedId1}`);
console.log(`Anonymized 2: ${anonymizedId2}`);

// Verification
console.log('Match:', anonymizedId1 === anonymizedId2);
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

/**
 * Anonymizes an Order ID deterministically.
 * * @param string $orderId   The original order ID.
 * @param string $secretKey A private key stored securely (e.g., in .env).
 * @return string           A consistent hex-encoded hash.
 */
function anonymizeOrderId(string $orderId, string $secretKey): string {
    // We use sha256 for a good balance of security and performance
    return hash_hmac('sha256', $orderId, $secretKey);
}

// Example Usage:
$mySecret = 'your-secure-system-key-here';
$originalId = 'ORD-99502';

$anonymized1 = anonymizeOrderId($originalId, $mySecret);
$anonymized2 = anonymizeOrderId($originalId, $mySecret);

echo "Original: " . $originalId . PHP_EOL;
echo "Anonymized 1: " . $anonymized1 . PHP_EOL;
echo "Anonymized 2: " . $anonymized2 . PHP_EOL;

if ($anonymized1 === $anonymized2) {
    echo "Success: The values are consistently mapped.";
}
```

{% endtab %}
{% endtabs %}


# Examples


# 8.SIMILAR

Typical request/response flow for similar products.

## GraphQL API Query

Get similar products for product with identifier `8S-DEMO-Polohemd-1`

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    similarProducts(first: 2) {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

## GraphQL API response

The API returns the first 2 similar products as requested.

```json
{
	"data": {
		"product": {
			"similarProducts": {
				"edges": [
					{
						"node": {
							"id": "8S-DEMO-Polohemd-10"
						}
					},
					{
						"node": {
							"id": "8S-DEMO-Polohemd-9"
						}
					}
				]
			}
		}
	}
}
```

## View event

User scrolls to the displayed similar items and items are about [50% inside the viewport](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent).

A view event with type `similarProducts` must contain a `similarProducts` property containing the identifier of the product the content was requested for. Here it is `8S-DEMO-Polohemd-1`, i.e. the same `id` as in our initial API query.

Also a `user` context need to be attached.    &#x20;

```json
{
  "type": "view",
  "view": {
    "type": "similarProducts",
    "similarProducts": {
      "id": "8S-DEMO-Polohemd-1",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

## Interact event

### click

User clicks on one of the shown similar products. Let's say `8S-DEMO-Polohemd-9` and no specific size is selected.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Polohemd-9"
    }
  },
  "context": [
    {
      "content": {
        "similarProducts": {
          "id": "8S-DEMO-Polohemd-1"
        },
        "type": "similarProducts"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

### add to cart

User adds one of the shown similar products to the cart. Let's say `8S-DEMO-Polohemd-10` in size L - e.g. the variant SKU is `8S-DEMO-Polohemd-10-L`.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "addToCart",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Polohemd-10-L"
    }
  },
  "context": [
    {
      "content": {
        "similarProducts": {
          "id": "8S-DEMO-Polohemd-1"
        },
        "type": "similarProducts"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```


# 8.SET (simplified)

Typical request/response flow for product sets.

## GraphQL API Query

Get matching product clusters for product with identifier `8S-DEMO-Polohemd-1`.\
We request 4 clusters with 2 products each, i.e. a total of 8 products.<br>

{% code expandable="true" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    productSets {
      edges {
        node {
          id
          tags
          imageLink
          products {
            id            
          }
        }
      }
    }
  }
}
```

{% endcode %}

## GraphQL API response

The API returns the first 2 product sets with 5 products each.

{% code expandable="true" %}

```json
{
  "data": {
    "product": {
      "productSets": {
        "edges": [
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
              "tags": ["everyday"],
              "imageLink": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg",
              "products": [
                {
                  "id": "8S-DEMO-Jacket-1"
                },
                {
                  "id": "8S-DEMO-Polohemd-1"
                },
                {
                  "id": "8S-DEMO-Gürtel-1"
                },
                {
                  "id": "8S-DEMO-Hose-1"
                },
                {
                  "id": "8S-DEMO-Schuhe-1"
                }
              ]
            }
          },
          {
            "node": {
              "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1",
              "tags": [],
              "imageLink": null,
              "products": [
                {
                  "id": "8S-DEMO-Jacket-2"
                },
                {
                  "id": "8S-DEMO-Polohemd-2"
                },
                {
                  "id": "8S-DEMO-Gürtel-2"
                },
                {
                  "id": "8S-DEMO-Hose-2"
                },
                {
                  "id": "8S-DEMO-Schuhe-2"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endcode %}

## View event

### initial view

User scrolls to the displayed product sets and items are about [50% inside the viewport](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent).

A view event with type `productSet` must contain a `productSet` property containing the identifier of the product set that was visible. Here it is the first set `e3bcaf66-5037-4d7b-be4a-8c571a6fb299`.

{% hint style="warning" %}
This differs from 8.SIMILAR and 8.SET - the `id` of the product set from the API response is now required here.
{% endhint %}

Also a `user` context need to be attached.

```json
{
  "type": "view",
  "view": {
    "type": "productSet",
    "productSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

### user paginates

The simplified 8.SET query returns product sets that can be displayed like they are. The most optimal way to display the sets is in pages for example in a slider. Thus the user will initially see the first set and can paginate to the next sets.

So on paging you need to send another view event.

The only thing that will change is the `productSet.id` this time it will be `e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1`

```json
{
  "type": "view",
  "view": {
    "type": "productSet",
    "productSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

## Interact event

### click

User clicks on one of the shown cross-selling products. Let's say `8S-DEMO-Gürtel-2` from the second set and no specific size is selected.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Gürtel-1"
    }
  },
  "context": [
    {
      "content": {
        "productSet": {
          "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1"
        },
        "type": "productSet"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

### add to cart

User adds one of the shown cross-selling products to the cart. Let's say `8S-DEMO-Gürtel-2` in size 120 - e.g. the variant SKU is `8S-DEMO-Gürtel-2-120`.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and **must contain** an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "addToCart",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Gürtel-3-120"
    }
  },
  "context": [
    {
      "content": {
        "productSet": {
          "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299#generated#1"
        },
        "type": "productSet"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```


# 8.SET

Typical request/response flow for matching product clusters.

## GraphQL API Query

Get matching product clusters for product with identifier `8S-DEMO-Polohemd-1`.\
We request 4 clusters with 2 products each, i.e. a total of 8 products.<br>

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    matchingProductClusters(first: 4) {
      edges {
        node {
          products(first: 2) {
            edges {
              node {
                id
              }
            }
          }
        }
      }
    }
  }
}

```

## GraphQL API response

The API returns the first 4 clusters with the first 2 products as requested.

{% code expandable="true" %}

```json
{
	"data": {
		"product": {
			"matchingProductClusters": {
				"edges": [
					{
						"node": {
							"products": {
								"edges": [
									{
										"node": {
											"id": "8S-DEMO-Polohemd-10"
										}
									},
									{
										"node": {
											"id": "8S-DEMO-Polohemd-8"
										}
									}
								]
							}
						}
					},
					{
						"node": {
							"products": {
								"edges": [
									{
										"node": {
											"id": "8S-DEMO-Hose-1"
										}
									},
									{
										"node": {
											"id": "8S-DEMO-Hose-3"
										}
									}
								]
							}
						}
					},
					{
						"node": {
							"products": {
								"edges": [
									{
										"node": {
											"id": "8S-DEMO-Schuhe-1"
										}
									},
									{
										"node": {
											"id": "8S-DEMO-Schuhe-2"
										}
									}
								]
							}
						}
					},
					{
						"node": {
							"products": {
								"edges": [
									{
										"node": {
											"id": "8S-DEMO-Jacket-1"
										}
									},
									{
										"node": {
											"id": "8S-DEMO-Jacket-2"
										}
									}
								]
							}
						}
					}
				]
			}
		}
	}
}
```

{% endcode %}

## View event

User scrolls to the displayed cross-selling items and items are about [50% inside the viewport](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent).

A view event with type `matchingProductClusters` must contain a `matchingProductClusters` property containing the identifier of the product the content was requested for. Here it is `8S-DEMO-Polohemd-1`, i.e. the same `id` as in our initial API query.

Also a `user` context need to be attached.

```json
{
  "type": "view",
  "view": {
    "type": "matchingProductClusters",
    "matchingProductClusters": {
      "id": "8S-DEMO-Polohemd-1",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

## Interact event

### click

User clicks on one of the shown cross-selling products. Let's say `8S-DEMO-Hose-1` and no specific size is selected.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Hose-1"
    }
  },
  "context": [
    {
      "content": {
        "matchingProductClusters": {
          "id": "8S-DEMO-Polohemd-1"
        },
        "type": "matchingProductClusters"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

### add to cart

User adds one of the shown cross-selling products to the cart. Let's say `8S-DEMO-Schuhe-1` in size 43 - e.g. the variant SKU is `8S-DEMO-Schuhe-1-43`.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and **must contain** an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "addToCart",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Schuhe-1-43"
    }
  },
  "context": [
    {
      "content": {
        "matchingProductClusters": {
          "id": "8S-DEMO-Polohemd-1"
        },
        "type": "matchingProductClusters"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```


# 8.SET Custom

Typical request/response flow for custom sets.

## GraphQL API Query

Get matching product clusters for product with identifier `8S-DEMO-Polohemd-1`.\
We request 4 clusters with 2 products each, i.e. a total of 8 products.<br>

{% code expandable="true" %}

```graphql
query {
  product(id: "8S-DEMO-Polohemd-1") {
    customSets {
      edges {
        node {
          id
          title
          description
          images {
            edges {
              node {
                url
              }
            }
          }
          products {
            id
            similarProducts(first: 2) {
              edges {
                node {
                  id
                }
              }
            }
          }
        }
      }
    }
  }
}
```

{% endcode %}

## GraphQL API response

The API returns the first 4 clusters with the first 2 products as requested.

{% code expandable="true" %}

```json
{
	"data": {
		"product": {
			"customSets": {
				"edges": [
					{
						"node": {
							"id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
							"title": "8SELECT Fashion for everyday",
							"description": "This is a demo set. It is here to preview the 8.SET Custom widget.",
							"images": {
								"edges": [
									{
										"node": {
											"url": "https://thumbnails.8scdn.io/9056af8c-e9b8-50db-bdf1-f794dc2e10d7.jpg"
										}
									}
								]
							},
							"products": [
								{
									"id": "8S-DEMO-Jacket-1",
									"similarProducts": {
										"edges": [
											{
												"node": {
													"id": "8S-DEMO-Jacket-2"
												}
											}
										]
									}
								},
								{
									"id": "8S-DEMO-Polohemd-1",
									"similarProducts": {
										"edges": [
											{
												"node": {
													"id": "8S-DEMO-Polohemd-10"
												}
											},
											{
												"node": {
													"id": "8S-DEMO-Polohemd-9"
												}
											}
										]
									}
								},
								{
									"id": "8S-DEMO-Gürtel-1",
									"similarProducts": {
										"edges": [
											{
												"node": {
													"id": "8S-DEMO-Gürtel-3"
												}
											},
											{
												"node": {
													"id": "8S-DEMO-Gürtel-4"
												}
											}
										]
									}
								},
								{
									"id": "8S-DEMO-Hose-1",
									"similarProducts": {
										"edges": [
											{
												"node": {
													"id": "8S-DEMO-Hose-2"
												}
											},
											{
												"node": {
													"id": "8S-DEMO-Hose-3"
												}
											}
										]
									}
								},
								{
									"id": "8S-DEMO-Schuhe-1",
									"similarProducts": {
										"edges": [
											{
												"node": {
													"id": "8S-DEMO-Schuhe-2"
												}
											},
											{
												"node": {
													"id": "8S-DEMO-Schuhe-3"
												}
											}
										]
									}
								}
							]
						}
					}
				]
			}
		}
	}
}
```

{% endcode %}

## View event

User scrolls to the displayed cross-selling items and items are about [50% inside the viewport](/api-tracking/events/view/how-to-evaluate-if-view-event-can-be-sent).

A view event with type `customSet` must contain a `customSet` property containing the identifier of the custom set. Here it is `e3bcaf66-5037-4d7b-be4a-8c571a6fb299`.

{% hint style="warning" %}
This differs from 8.SIMILAR and 8.SET - the `id` of the custom set from the API response is now required here.
{% endhint %}

Also a `user` context need to be attached.

```json
{
  "type": "view",
  "view": {
    "type": "customSet",
    "customSet": {
      "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299",
    }
  },
  "context": [
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

## Interact event

### click

User clicks on one of the shown cross-selling products. Let's say `8S-DEMO-Gürtel-1` and no specific size is selected.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and can contain an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "click",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Gürtel-1"
    }
  },
  "context": [
    {
      "content": {
        "customSet": {
          "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299"
        },
        "type": "customSet"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```

### add to cart

User adds one of the shown cross-selling products to the cart. Let's say `8S-DEMO-Gürtel-3` in size 120 - e.g. the variant SKU is `8S-DEMO-Gürtel-3-120`.

An interact event with type `product` must contain a `product` property containing the `sku` of the respective product and **must contain** an `action`.

Also a `content` and `user` context need to be attached.\
The `content` context basically resembles the data from the view event.

```json
{
  "type": "interact",
  "interact": {
    "action": "addToCart",
    "type": "product",
    "product": {
      "sku": "8S-DEMO-Gürtel-3-120"
    }
  },
  "context": [
    {
      "content": {
        "customSet": {
          "id": "e3bcaf66-5037-4d7b-be4a-8c571a6fb299"
        },
        "type": "customSet"
      },
      "type": "content"
    },
    {
      "type": "user",
      "user": {
        "id": "c57a43f7-eefc-462b-b5a8-0ef421e90f67"
      }
    }
  ]
}
```


