> For the complete documentation index, see [llms.txt](https://docs.abtasty.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.abtasty.com/recommendations-and-merchandising/how-tos/how-to-integrate-merchandising-via-api.md).

# How to integrate Merchandising via API

### Overview

The Merchandising feature delivers curated product lists, called strategies, configured in the AB Tasty back-office.

Two endpoints are available depending on what you already know about the strategy:

* **By category ID:** use this endpoint when your front-end only knows the current category. The API will resolve the bound strategy automatically.
* **By strategy UUID:** use this endpoint when the UUID is already known (cached, configured, etc.).

Both endpoints return the same payload shape (items, facets, pagination) and support the same query parameters.

Base URL: `https://uc-info.eu.abtasty.com/v1`

### Authentication

All requests require a JWT in the `Authorization` header:

```
Authorization: Bearer <token>
```

### Endpoints

#### 1. Fetch items by category ID

```
GET /reco/{site_id}/merch/category_id/{category_id}
```

Resolves the merchandising strategy associated with the  `category_id` and returns its ranked products in a single call.

**Path parameters**

| Parameter     | Type    | Description                   |
| ------------- | ------- | ----------------------------- |
| `site_id`     | integer | Your AB Tasty site identifier |
| `category_id` | string  | The category identifier       |

Query parameters, response payload and error codes are identical to the by-UUID endpoint, with one additional error:

| Status | Error code         | Reason                                                        |
| ------ | ------------------ | ------------------------------------------------------------- |
| 400    | `UNKNOWN_CATEGORY` | No strategy is bound to this `category_id` for this `site_id` |

#### 2. Fetch items by strategy UUID

```
GET /reco/{site_id}/merch/{merch_id}
```

Returns the ranked list of products for a specific strategy, including optional filtering, faceting and pagination. Use this endpoint when the strategy UUID is already known.

**Path parameters**

| Parameter  | Type    | Description                        |
| ---------- | ------- | ---------------------------------- |
| `site_id`  | integer | Your AB Tasty site identifier      |
| `merch_id` | string  | UUID of the merchandising strategy |

**Query parameters**

| Parameter       | Type       | Default  | Description                                                                                                                  |
| --------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `variables`     | JSON       | —        | JSON object injected into the strategy template. Ex: `variables={"viewed_items": ["123","789"], "viewing_item": "456"}`      |
| `fields`        | JSON array | —        | JSON array of product fields to return. Ex: `["id","title","price"]`. `id` is always included.                               |
| `page`          | integer    | `null`   | Page number (1-indexed). Ignored when `offset` is also provided.                                                             |
| `offset`        | integer    | `0`      | Zero-based item offset. Takes precedence over `page`.                                                                        |
| `limit`         | integer    | **`20`** | Number of items per page. **Defaults to `20` when omitted.** Use `0` for no limit.                                           |
| `facets`        | boolean    | `false`  | Set to `true` to include facet counts in the response.                                                                       |
| `filters[f][]`  | string     | —        | Filter on field `f`. Repeat for multiple values (OR within a field, AND across fields). See filter syntax below.             |
| `sort[f]`       | string     | —        | Override the strategy's ordering. `f` is the field name, value is `asc` or `desc` (case-insensitive). See sort syntax below. |
| `output_format` | string     | `json`   | Response format. Accepted values: `json`, `csv`.                                                                             |
| `delimiter`     | string     | `;`      | CSV delimiter (only used when `output_format=csv`). Accepted values: `,`, `;`, `%09`, \`                                     |

These query parameters apply to **both endpoints** (UUID and category).

**Filter syntax**

Filters use PHP-style bracket notation. Two syntaxes are supported depending on the operation needed.

**Combining rules**

* Multiple values on the **same field** → combined with **OR**
* Filters on **different fields** → combined with **AND**
* Field names must match `^[A-Za-z_][A-Za-z0-9_]*$` (letters, digits and underscores only)

***

**Syntax 1 - Equality / IN (empty index `[]`)**

Use `filters[field][]=value` for equality. Repeat the parameter on the same field to produce an `IN` (OR) filter.

| Operation | Query string                                          | Behaviour                        |
| --------- | ----------------------------------------------------- | -------------------------------- |
| Equals    | `filters[available][]=1`                              | `available = 1`                  |
| IN (OR)   | `filters[category][]=shoes&filters[category][]=boots` | `category IN ('shoes', 'boots')` |

***

**Syntax 2 - Comparison operators (indexed entries)**

Use `filters[field][{idx}][operator]` + `filters[field][{idx}][value]` for `>`, `<`, `>=`, `<=` comparisons. Use incrementing indices (`0`, `1`, …) to stack multiple conditions on the same field.

Allowed operators: `=`, `>`, `<`, `>=`, `<=`

| Operation          | Query string                                                                                                                                                               |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Greater than       | `filters[price][0][operator]=>&filters[price][0][value]=10`                                                                                                                |
| Less than or equal | `filters[price][0][operator]=<=&filters[price][0][value]=500`                                                                                                              |
| Range (min + max)  | <p><code>filters\[price]\[0]\[operator]=>=\&filters\[price]\[0]\[value]=10</code><br><code>\&filters\[price]\[1]\[operator]=<=\&filters\[price]\[1]\[value]=500</code></p> |

**Combined example** in-stock shoes priced between €10 and €500:

```
filters[available][]=1
&filters[category][]=shoes&filters[category][]=boots
&filters[price][0][operator]=>=&filters[price][0][value]=10
&filters[price][1][operator]=<=&filters[price][1][value]=500
```

**Sort syntax**

The `sort[field]=direction` parameter **replaces the strategy's own ordering** (pins included). The strategy's catalog and your active `filters[...]` still apply.

```
?sort[price]=desc
?sort[created_at]=asc
```

* Only **one** sort field per request.
* Direction is `asc` or `desc`, case-insensitive.
* The field must be flagged **`merch_sortable`** for your site in the AB Tasty back-office (this is a dedicated flag, distinct from the internal `sortable` flag). Requesting an unmarked field returns `400 INVALID_SORT_FIELD`.

**Sort-specific error codes** (all returned as `400 Bad Request`)

| Error code               | Reason                                                 |
| ------------------------ | ------------------------------------------------------ |
| `INVALID_SORT_SYNTAX`    | Malformed `sort[...]` key, or more than one sort entry |
| `INVALID_SORT_FIELD`     | Field is not sortable for this site                    |
| `INVALID_SORT_DIRECTION` | Direction is not `asc` / `desc`                        |
| `SORT_UNSUPPORTED`       | The strategy does not support runtime sorting          |

**Response (JSON)**

```json
{
  "name": "strategy-name",
  "items": [
    {
      "id": "product-123",
      "title": "Running Shoes",
      "price": 89.99
    }
  ],
  "total_items": 248,
  "total_pages": 13,
  "current_page": 1,
  "limit": 20,
  "has_next": true,
  "facets": {
    "category": {
      "shoes": 84,
      "boots": 40
    },
    "available": {
      "1": 248
    }
  }
}
```

**Response fields**

| Field          | Type    | Description                                                         |
| -------------- | ------- | ------------------------------------------------------------------- |
| `name`         | string  | Internal name of the merchandising strategy                         |
| `items`        | array   | Ordered list of products for the requested page                     |
| `total_items`  | integer | Total number of products matching the strategy and active filters   |
| `total_pages`  | integer | Total number of pages given the current `limit`                     |
| `current_page` | integer | Current page number (1-indexed)                                     |
| `limit`        | integer | Number of items per page used for this response                     |
| `has_next`     | boolean | Whether a next page exists                                          |
| `facets`       | object  | Facet counts by field and value. Only populated when `facets=true`. |

**Response statuses**

| Response Status | Message              | Reasons                                                                                   |
| --------------- | -------------------- | ----------------------------------------------------------------------------------------- |
| 200             | OK                   |                                                                                           |
| 400             | Bad Request          | Strategy not found for this `merch_id`; invalid filter field; query execution error       |
| 403             | Forbidden            | Missing token, invalid/expired token, or `site_id` mismatch                               |
| 422             | Unprocessable Entity | `variables` is not valid JSON; `fields` is not a valid JSON list; unknown `output_format` |
| 503             | Service Unavailable  | Request exceeded the 10-second global timeout                                             |

### Typical integration flow

Pick one of the two entry points depending on what your front-end has on hand.

**A. You only know the category ID** - single call:

```
GET /reco/{site_id}/merch/category_id/{category_id}?fields=["id","title","price"]&limit=20&facets=true
→ items[], facets{}, total_items, total_pages
```

**B. You already have the strategy UUID** - single call:

```
GET /reco/{site_id}/merch/{uuid}?fields=["id","title","price"]&limit=20&facets=true
→ items[], facets{}, total_items, total_pages
```

**Then, for both:**

```
1. Display facets and let the user apply filters
   GET ...&filters[category][]=shoes&filters[available][]=1
   → filtered items[] and updated facets{}

2. Paginate
   GET ...&page=2
   — or —
   GET ...&offset=20&limit=20
```

### Notes

* The `id` field is always returned regardless of the `fields` parameter.
* When both `offset` and `page` are provided, `offset` takes precedence.
* **`limit` defaults to `20` when omitted.** Pass `limit=0` to return all matching items with no pagination cap.
* Facet computation adds overhead, only request `facets=true` when you need to render facet counts.
* **Per-field opt-in is required in the AB Tasty back-office:**
  * `filters` and `facets` only accept fields flagged as **`facetable`**.
  * `sort` only accepts fields flagged as **`merch_sortable`** (distinct from the internal `sortable` flag, they are not interchangeable). Requesting a non-opted-in field returns `400`.
* The category endpoint returns `400 UNKNOWN_CATEGORY` (not `200` with an empty payload) when no strategy is bound to the category.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.abtasty.com/recommendations-and-merchandising/how-tos/how-to-integrate-merchandising-via-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
