# How to integrate merchandising by API

### Overview <a href="#overview" id="overview"></a>

The Merchandising feature surfaces curated product lists (strategies) configured in the AB Tasty back-office.

Two API calls are needed :

1. **Resolve a strategy UUID** from a category ID (optional : skip if the UUID is already known)
2. **Fetch the strategy items** with optional filtering, faceting and pagination

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

### Authentication <a href="#authentication" id="authentication"></a>

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

`Authorization: Bearer <token>`

### Endpoints <a href="#endpoints" id="endpoints"></a>

#### 1. Resolve strategy UUID from category ID <a href="#id-1.-resolve-strategy-uuid-from-category-id" id="id-1.-resolve-strategy-uuid-from-category-id"></a>

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

Use this endpoint when you only know the category ID and need to find the strategy UUID to call the items endpoint.

**Path parameters**

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

**Response**

`{ "uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }`

Use the returned `uuid` as `merch_id` in the items endpoint below.

{% hint style="info" %}
When no strategy is linked to the given `category_id`, the endpoint still returns `200` with `{"uuid": null}` rather than a 404.
{% endhint %}

**Response statuses**

| Response Status | Message   | Reasons                                                            |
| --------------- | --------- | ------------------------------------------------------------------ |
| 200             | OK        | Always returned, even when no strategy is found (`uuid` is `null`) |
| 403             | Forbidden | Missing token, invalid/expired token, or `site_id` mismatch        |

#### 2. Fetch merchandising strategy items <a href="#id-2.-fetch-merchandising-strategy-items" id="id-2.-fetch-merchandising-strategy-items"></a>

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

Returns the ranked list of products for a given strategy, with optional filtering, faceting and pagination.

**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       | -       | Ex: variables={“viewed\_items”: \[“123”,”789”],”viewing\_item”: “456“}                                           |
| `fields`        | JSON array | -       | 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    | null    | Number of items per page.                                                                                        |
| `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. |
| `output_format` | string     | `json`  | Response format. Accepted values: `json`, `csv`.                                                                 |
| `delimiter`     | string     | `;`     | CSV delimiter (only used when `output_format=csv`). Accepted values : `,`, `;`, `%09`, `\|`                      |

**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`

**Response (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 <a href="#typical-integration-flow" id="typical-integration-flow"></a>

`1. (Optional) Resolve UUID GET /reco/{site_id}/merch/category_id/{category_id} → { "uuid": "3fa85f64-..." } 2. Fetch first page of items GET /reco/{site_id}/merch/{uuid}?variables=category_id:shoes&fields=id,title,price&limit=20&facets=true → items[], facets{}, total_items, total_pages 3. Display facets and let user apply filters GET /reco/{site_id}/merch/{uuid}?...&filters[category][]=shoes&filters[available][]=1 → filtered items[] and updated facets{} 4. Paginate GET /reco/{site_id}/merch/{uuid}?...&page=2 — or — GET /reco/{site_id}/merch/{uuid}?...&offset=20&limit=20`

### Notes <a href="#notes" id="notes"></a>

* The `id` field is always returned regardless of the `fields` parameter.
* When both `offset` and `page` are provided, `offset` takes precedence.
* Facet computation adds overhead - only request `facets=true` when you need to render facet counts.
* Field names in `filters` and `facets` must match the fields configured as “facetable” in the AB Tasty back-office.


---

# Agent Instructions: 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-by-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.
