---
title: Recommendations Customization
description: Recommendations Customization - Luigi's Box API reference.
slug: recommendations/customization
docKind: hub
hub: recommendations
tableOfContents: false
---

# Recommendations Customization

## Overview

Luigi’s Box Recommendations Customizations API allows you to manually override the automated recommendation algorithms. You can promote specific items (upsell), enforce brand consistency, or curate seasonal collections by "pinning" items to specific positions in the result set.

To use this feature, a recommender must be initialized. See [Recommender API](/recommendations/api/v1/recommender/) for more details.

:::caution
We strongly recommend using the Luigi's Box App UI instead of this API. Manage customizations at `https://app.luigisbox.com/sites/{TRACKER_ID}/setup/recommendations_customization` when possible. It allows you to manage recommendation pinning without programming effort.
:::

Check the [OpenAPI Specification](/assets/openapi/recommendations-customizations-api.yaml) for the
formal contract in YAML.

## Core concepts

To effectively use this API, you must understand the relationship between the following components:

* **Customization (Scope):** This represents the **Context** or the **Trigger**. It defines *when* a set of rules should apply. For example, "Only apply these rules when the user is looking at a Fender Guitar" or "Apply these rules to the Basket page."
* **Pin:** This represents the **Action** or the **Result**. It defines *what* items should appear. For example, "Show this specific guitar strap in position 1."
* **Target:** The specific rule used to trigger the Customization (e.g., a specific Item ID or a Category).

<div class="lb-mermaid-frame"><pre class="mermaid">flowchart TD
Tracker["Tracker or store"] --> Model["Recommender model"]
Model --> Cust1["Customization 1: target item A"]
Model --> Cust2["Customization 2: target brand Nike"]
Cust1 --> Pin1["Pin: show item X at position 1"]
Cust1 --> Pin2["Pin: show item Y at position 2"]
Cust2 --> Pin3["Pin: show item Z at position 1"]</pre></div>

---

## 1. Defining the Scope (The "When")

Every customization is attached to a specific Recommender Model (e.g., `basket`, `product_detail`). Within that model, you can define **Target Types** to control specificity.

### Scope priority

When a recommendation request matches multiple customizations, the **Priority** determines which one wins:

1. **Item Target (Highest):** Matches a specific Item ID (e.g., "User is viewing Product A").
2. **Criteria Target:** Matches item attributes (e.g., "User is viewing any item where Brand is Nike").
3. **All (Lowest):** Applies globally to the model (e.g., "Always apply on the Homepage").

### The customization object

The Customization Object (often referred to as the "Scope") is the parent container. It holds the target definition and the list of pins.

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `id` | UUID | Read-only | Unique identifier. |
| `model` | String | Yes | Recommender model name (e.g., `basket`). |
| `target_type` | String | Yes | `item`, `criteria`, or `all`. |
| `target_identity` | String | Cond. | The item ID (ID) if `target_type` is `item`. |
| `target_criteria` | Object | Cond. | The [Criteria Object](#the-criteria-object) if `target_type` is `criteria`. |
| `tags` | Array | No | List of organizational tags. |
| `pin_definitions` | Array | Yes | List of [Pin Objects](#the-pin-object). |
| `creator` | String | Read-only | User who created the customization. |

### Examples

**Target: Item**

```json
{
  "id": "199620b4-...",
  "model": "basket",
  "target_type": "item",
  "target_identity": "/p/123",
  "tags": ["guitar"],
  "pin_definitions": [ ... ]
}
```

**Target: Criteria**

```json
{
  "id": "199620b4-...",
  "model": "basket",
  "target_type": "criteria",
  "target_criteria": {
    "attribute": "brand",
    "operator": "in",
    "values": ["Fender"]
  },
  "pin_definitions": [ ... ]
}
```

**Target: All**

```json
{
  "id": "199620b4-...",
  "model": "basket",
  "target_type": "all",
  "pin_definitions": [ ... ]
}
```

## 2. Defining the Pins (The "What")

Once a Customization is triggered, the Pins determine which items are injected into the results.

### Pin types

* **Item Pin:** Injects a specific product ID.
* **Criteria Pin:** Injects items that match specific metadata (e.g., "Any item that is Red"). Note: This is computationally expensive.

### Pin behavior and positioning

* **Regular (position: N):** Forces the item into the Nth slot (1-based index).
* **Global (position: -1):** Attempts to pin the item in every position (usually used with Criteria pins to flood results).
* **Block (position: 0):** Acts as a Blacklist. Prevents the defined item or criteria from appearing in the recommendations.

### The pin object

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `position` | Integer | Yes | 1-based index. Use -1 for Global, 0 for Block. |
| `pin_type` | String | Yes | `item` or `criteria`. |
| `pin_identity` | String | Cond. | Item ID if `pin_type` is `item`. |
| `pin_criteria` | Object | Cond. | Criteria Object if `pin_type` is `criteria`. |
| `active_from` | Date | No | ISO 8601 Start date. |
| `active_to` | Date | No | ISO 8601 End date. |
| `is_block_pin` | Boolean | No | If `true`, excludes matching items. |

### Examples

**Pin Type: Item**

```json
{
  "position": 1,
  "pin_type": "item",
  "pin_identity": "/p/234",
  "active_from": "2025-01-22T08:49:58Z",
  "is_block_pin": false
}
```

**Pin Type: Criteria**

```json
{
  "position": 4,
  "pin_type": "criteria",
  "pin_criteria": {
    "attribute": "color",
    "operator": "in",
    "values": ["red"]
  }
}
```

## 3. Resolving Collisions

Because multiple customizations can target the same context, conflicts can occur. The system resolves them using a **Priority Selection & Shifting** strategy.

<div class="lb-mermaid-frame"><pre class="mermaid">flowchart TD
Start{"Conflict detected?"}
Start -- No --> Apply["Apply pin normally"]
Start -- Yes --> Type{"Collision type"}
Type -- Same position --> Select["Select pins with highest priority"]
Select --> Order["Order by recency"]
Order --> Return["Return selected pins and shift downstream pins"]
Type -- Duplicate item --> LocCheck{"Same scope?"}
LocCheck -- Yes --> MinPos["Use minimum position"]
LocCheck -- No --> HighScope["Use pin from higher-priority scope"]
Type -- Block vs regular --> BlockScope{"Does the regular pin have higher scope priority?"}
BlockScope -- Yes --> RegularWins["Regular pin overrides block"]
BlockScope -- No --> BlockWins["Item is blocked and removed"]</pre></div>

### Scenario A: Collision on position

**Situation:**

* **Pins A & B** (High Priority, e.g., Item Scope) want **Position 2**.
* **Pin C** (Low Priority, e.g., Criteria Scope) also wants **Position 2**.
* **Pin X** (Any Priority) wants **Position 3**.

**Resolution:**

1.  **Select Winner:** The system selects *only* the pins with the highest priority (Pins A & B). **Pin C is ignored completely** because it lost the priority battle for Position 2.
2.  **Order Winners:** A and B are ordered by recency (e.g., input order).
3.  **Shift Downstream:** Because A and B now occupy **Position 2 and 3**, the original **Pin X** (which wanted Position 3) is **pushed down to Position 4**.

**Result:**

* **Pos 2:** Pin A
* **Pos 3:** Pin B
* **Pos 4:** Pin X (Shifted)
* *(Pin C is discarded)*.

### Scenario B: Duplicates

**Situation:** The same item is pinned in multiple places.

**Resolution:**

* **Same Scope:** The item appears at the **minimum** (lowest number) position defined.
* **Different Scopes:** The pin from the **highest priority** scope is respected.

### Scenario C: Block vs. regular

**Situation:** One rule says "Show Item A", another says "Block Item A".

**Resolution:** The **Regular** pin overrides the Block pin *only* if its scope has a **higher priority**. Otherwise, the Block pin wins, and the item is **completely excluded** from the recommendation results.

## 4. Advanced Logic: The Criteria Object

The Criteria Object allows defining complex rules using logic (`and`, `or`, `not`) and attribute comparisons. It is used in `target_criteria` (Customization) and `pin_criteria` (Pin).

### Structure

The top level must be a **Logical** operator or an **Attribute** condition.

#### 1. Logical Criteria

Combines multiple criteria using boolean logic.

| Field | Type | Description |
| :--- | :--- | :--- |
| `operator` | String | `and`, `or`, `not` (unary). |
| `criteria` | Array | List of nested criteria objects. |

**Example:**

```json
{
  "operator": "or",
  "criteria": [
    {
      "attribute": "color",
      "operator": "in",
      "values": ["red", "black"]
    },
    {
      "operator": "and",
      "criteria": [
        {
          "attribute": "gender",
          "operator": "in",
          "values": ["woman", "unisex"]
        },
        {
          "attribute": "size",
          "operator": "lte",
          "values": [38]
        }
      ]
    }
  ]
}
```

#### 2. Attribute Criteria

Basic criteria comparing item attributes to static values.

| Field | Description |
| :--- | :--- |
| `attribute` | The name of the attribute (e.g., `brand`). |
| `operator` | Comparison operation. |
| `values` | List of values to match against. |
| `condition` | Optional dynamic condition (see below). |
| `transformation` | Optional value transformation (see below). |

**Supported Operators:**

| Type | Operators |
| :--- | :--- |
| Text/Boolean | `in`, `not_in`, `all_of`, `exists`, `not_exists` |
| Numeric/Date | `lt`, `lte`, `gt`, `gte` |

**Example:**

```json
{
  "attribute": "color",
  "operator": "in",
  "values": ["red", "black"]
}
```

#### 3. Type Criteria

Matches specific item types (`product`, `variant`, `query`, `category`, `article`).

| Field | Description |
| :--- | :--- |
| `operator` | `in`, `not_in` |
| `values` | List of types (e.g., `["product", "variant"]`). |
| `condition` | Optional dynamic condition. |

### Criteria condition (dynamic)

Allows comparing an attribute against a value derived from the current context (e.g., "Recommend items with the same brand as the current item").

| Field | Description |
| :--- | :--- |
| `left_attribute` | Attribute name to use for validation. |
| `left_value` | Placeholder referencing which part of the attribute to use (e.g., `@same`). |
| `operator`       | Supported operators: `in`, `not_in`, `all_of`, `exists`, `not_exists` (text/boolean); `lt`, `lte`, `gt`, `gte`, `between`, `eq`, `neq` (numeric/date). |
| `right_values` | Values to match against the left side. |
| `transformation` | Optional value transformation (see below). |

**Supported `left_value` placeholders:**

- `@same`: Exact value of the attribute.
- `@same_first`, `@same_last`: First or last value of a list.
- `@same_second`, `@same_third`: Second or third value of a list.
- `@same_first_two`, `@same_first_three`: First two or three values of a list.
- `@same_second_and_later`, `@same_third_and_later`: All values from the second or third onward.
- `@same_but_last`: All values except the last.
- `*_main_only`: Suffix to target only the first list in multidimensional attributes (e.g., `@same_first_main_only`).

**Example:**

```json
{
  "left_attribute": "_category",
  "left_value": "@same",
  "operator": "in",
  "right_values": ["Shoes"]
}
```

### Transformations

Transforms the value before comparison.

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `function` | String | ✓ | The operation to perform (e.g., `+`, `*`). |
| `value` | Number | | The argument for the function (e.g., `42`, `-0.8`). |

**Supported Functions:**

* **Numeric:**
    * `+`: Add the `value` to the original number.
    * `*`: Multiply the original number by the `value`.
* **Date:**
    * `+`: Add `value` (in days) to the original date.
* **Text/Array:**
    * `length`: Count the characters or array items. *(No `value` required)*.
**Example:**

```json
{
  "function": "+",
  "value": -5
}
```
