---
title: Recommender tutorial
description: A step-by-step guide to integrating personalized recommendation boxes, dataLayer events, and batching with Luigi's Box.
slug: tutorials/recommender
docKind: tutorial
hub: tutorials
---

## Recommender boxes

This tutorial will guide you through the integration of a recommender experience and show interactions between different services and APIs.

Use the recommender service to implement personalized recommender boxes such as the one depicted in the image.

![Recommender](/images/flows/recommender.png)

## Define recommender models

To start, you will need a recommender model which specifies the logic used to select the products into the recommender box.

You can create the models yourself in the [Luigi's Box application](https://app.luigisbox.com/) in the Recommendations > Recommenders setup section.

![Recommender setup](/images/flows/recommender_setup.png)

Click the "Add new recommender" button and follow the instructions.

![Recommender model](/images/flows/recommender_model.png)

When you are done with the setup you will see the recommender model name assigned by the system (such as `item_detail_complements`). Note it down as you will need it to make API requests.

## Make a request to recommender API

Make a request to the recommender API. This is a POST request with a JSON payload.

```http
POST https://live.luigisbox.com/v1/recommend?tracker_id=<tracker_id>
[
  {
    "item_ids": ["PR-15553"],
    "recommendation_type": "item_detail_complements",
    "recommender_client_identifier": "item_detail_complements",
    "size": 4,
    "user_id": "<transient_user_id>"
  }
]
```

:::tip
Note that the payload is an array, as you can ask for several recommendations in a single request (more on that later).

`item_ids` is the array of identities for which you are asking the recommendations. They must point to valid catalog identities. You will typically see these scenarios: empty array for cases where there is no input item (such as when recommending on a home page), a single-element array (when recommending for a specific product) or multi-element array (when recommending for several products at once, typically in the basket).

`recommendation_type` is the model name you have obtained in the previous step.

`recommender_client_identifier` is the recommender name for analytics. Feel free to use the same name as for the `recommendation_type`.

`user_id` is the transient user id from the `_lb` cookie (unless the user is signed in, more on that later).
:::

## Render recommendation box

The recommender API response includes a metadata fields and the `hits` field which you should be already familiar with from implementing autocomplete and search endpoints. The structure of the `hits` attribute contains all the product data you have indexed.

![Recommender](/images/flows/recommender.png)

```json
[
    {
        "generated_at": "2025-01-06T23:34:59.585570",
        "hits": [
            {
                "url": "PR-100659",
                "attributes": {
                    "image_link": "https://cdn.myshoptet.com/usr/demoshop.luigisbox.com/user/shop/detail/1778046_gibson-les-paul-studio.jpg",
                    "title": "Gibson Les Paul Studio",
                    "price_amount": 1359,
                    "web_url": "/gibson-les-paul-studio/"
                },
                "type": "item"
            },
            {
                "url": "PR-70615",
                "attributes": {
                    "image_link": "https://cdn.myshoptet.com/usr/demoshop.luigisbox.com/user/shop/detail/1777002_chapman-guitars-ml1-modern-baritone.jpg",
                    "title": "Chapman Guitars ML1 Modern Baritone",
                    "price_amount": 598,
                    "web_url": "/chapman-guitars-ml1-modern-baritone/"
                },
                "type": "item"
            }
        ],
        "recommendation_id": "e9d6957a-f471-4ea9-be7a-770c32a94afa",
        "recommendation_type": "item_detail_complements",
        "recommender": "item_detail_complements",
        "recommender_client_identifier": "item_detail_complements",
        "recommender_version": "04563f77e",
        "user_id": "7456102757361609000"
    }
]
```

API response shortened for brevity.

## Fire a dataLayer event for recommender

After the recommendations have been rendered, fire a [Recommendation dataLayer event](/analytics/collector/#recommender-example) describing what you have just rendered.

![Recommender](/images/flows/recommender.png)

```javascript
dataLayer.push({
    event: "view_item_list",
    ecommerce: {
        item_list_name: "Recommendation",
        items: [
            {
                item_id: "PR-100659",
                item_name: "Gibson Les Paul Studio",
                index: 1,
                price: 1359
            },
            {
                item_id: "PR-70615",
                item_name: "Chapman Guitars ML1 Modern Baritone",
                index: 1,
                price: 598
            }
        ],
        filters: {
            "RecommenderClientId": "item_detail_complements",
            "ItemIds": ["PR-15553"]
        }
    }
});
```

dataLayer event shortened for brevity.

:::tip
Make sure that the `item_id` refers to the object identity (the `url` attribute in the top-level object data). The dataLayer event structure is the same as for Autocomplete, except the `item_list_name`. Make sure to use `Recommendation` here. The `RecommenderClientId` in the filters field refers to the analytics identifier you have used in the `recommender_client_identifier` in the request data. The `ItemIds` array should echo the `item_ids` you have used in the request.
:::

## Fire dataLayer click event

When the user clicks on a recommendation, immediately fire a dataLayer event.

![Recommender click](/images/flows/recommender_click.png)

```javascript
dataLayer.push({
    event: "select_item",
    ecommerce: {
        items: [
            {
                item_id: "PR-70615",
            }
        ]
    }
});
```

## Fire dataLayer customer_id event

```javascript
dataLayer.push({
    event: "luigisbox.collector.customer_id",
    customer_id: "281827"
});
```

Once you know the personalized user ID, emit a [customer_id dataLayer event](/analytics/collector/#user-identifiers). It is safe to emit the event just once per session, but it will not cause any problems if you emit it more than once per session.

![Sign in](/images/flows/sign_in.png)

## Make a request to recommender API for identified user

Make a request to the recommender API. This is a POST request with a JSON payload. Note that the only difference is in the value of the `user_id` parameter which now points to your internal user ID which you propagated to the dataLayer.

```http
POST https://live.luigisbox.com/v1/recommend?tracker_id=<tracker_id>
[
  {
    "item_ids": ["PR-15553"],
    "recommendation_type": "item_detail_complements",
    "recommender_client_identifier": "item_detail_complements",
    "size": 4,
    "user_id": "<persistent_user_id>"
  }
]
```

:::tip
`user_id` is the persistent user id. It must be the same user ID you are propagating in the dataLayer event.
:::

## Batch multiple recommendations into a single API request

For cases where you need to request recommendations for several different models, it is more efficient to issue a single request. This will be typically useful for scenarios where you are rendering several recommender boxes on a single page.

By asking for several recommendations in a single request you will also **automatically avoid duplicate recommendations**.

```http
POST https://live.luigisbox.com/v1/recommend?tracker_id=<tracker_id>
[
  {
    "item_ids": ["PR-15553"],
    "recommendation_type": "item_detail_complements",
    "recommender_client_identifier": "item_detail_complements",
    "size": 4,
    "user_id": "<transient_user_id>"
  },
  {
    "item_ids": [],
    "recommendation_type": "last_seen",
    "recommender_client_identifier": "last_seen",
    "size": 6,
    "user_id": "<transient_user_id>"
  }
]
```

:::tip
Note that the payload is an array, as you can ask for several recommendations in a single request.
:::

## Limit amount of data transferred

To limit the amount of data transferred between systems, specify the `hit_fields` attribute in the API request. It is an array of result properties which will be included in the API response. By using this, you can significantly reduce the amount of data transfer and increase performance.

```http
POST https://live.luigisbox.com/v1/recommend?tracker_id=<tracker_id>
[
  {
    "item_ids": ["PR-15553"],
    "recommendation_type": "item_detail_complements",
    "recommender_client_identifier": "item_detail_complements",
    "size": 4,
    "user_id": "<transient_user_id>",
    "hit_fields": ["title", "image_link", "description", "price_amount"]
  }
]
```

:::tip
Notice the `hit_fields` attribute.
:::

## What's next?

Continue by implementing the [product listings](/tutorials/plp/).

![Product listing](/images/flows/plp.png)
