---
title: Shopping Assistant implementation flow
description: Plan the request, state, rendering, and edge-case flow for a custom Shopping Assistant integration.
slug: shopping-assistant/guides/implementation-flow
docKind: guide
hub: shopping-assistant
---

Use this guide when you are designing the shape of a custom Shopping Assistant integration before writing the UI.

For a hands-on walkthrough with runnable examples, continue with [Building a custom shopping assistant](/quickstart/shopping-assistant/building-custom-ui/). For exact request and response fields, use the [Assistant API reference](/shopping-assistant/api/assistant/).

## What the Shopping Assistant does

The Shopping Assistant leads a shopper to the right product through a short guided dialogue. The shopper answers a handful of targeted questions — budget, use case, size, style — and the catalog narrows down after every answer.

Each assistant is a dialogue flow you design and publish in the [Luigi's Box app](https://app.luigisbox.com). The Assistant API runs that flow at request time: it tells your frontend which question to show next and which products currently match the shopper's selections. Your integration renders the flow, tracks selections, and sends product outcome events back to Luigi's Box so the assistant can learn.

## Before you start

Create and publish the assistant in the [Luigi's Box app](https://app.luigisbox.com). The API runs an existing assistant flow; it does not create or edit the questions.

You need:

| Requirement | Where to use it |
| :-- | :-- |
| `tracker_id` | Query parameter on every Assistant API request. |
| `assistant_handle` | Query parameter that selects the assistant to run. |
| Stable `user_id` | Query parameter used to connect the assistant flow to the same user or session. |
| `assistant_version` | Request body field. Use `-1` for the latest published version. The complete parameter list is in the [API reference](/shopping-assistant/api/assistant/#request-parameters). |

## The integration loop

The Shopping Assistant is a guided dialogue. The shopper sees a question with a set of options. When they pick an option, the product list updates to reflect that choice and the next question appears. This continues until no questions remain, at which point the shopper sees a final set of recommended products.

The API is stateless. Your frontend keeps track of which options the shopper has selected so far and sends that full selection history on every request. The API uses it to determine two things: which question to show next, and which products currently match.

<div class="lb-mermaid-frame"><pre class="mermaid">sequenceDiagram
participant Shopper
participant UI as Your frontend
participant API as Assistant API
UI->>API: First request with steps: []
API-->>UI: Question and matching hits
UI-->>Shopper: Show question and products
Shopper->>UI: Selects an option
UI->>UI: Append selection to steps
UI->>API: Next request with full steps array
API-->>UI: Next question and updated hits
Note over Shopper,API: Repeat until the API returns no next question
API-->>UI: question: null, and final hits
UI-->>Shopper: Show final recommendations</pre></div>

The integration follows this loop:

1. **Initialize**: Call the API with an empty history.
2. **Render**: Display the returned question and matching products.
3. **Capture**: When the shopper selects an option, record it in your local state.
4. **Advance**: Call the API again, passing the full updated history.
5. **Complete**: Repeat until the API returns no further questions.

## 1. Managing client state

Maintain a small, explicit state object throughout the shopper's session:

| State | Why it matters |
| :-- | :-- |
| `user_id` | Connects assistant requests from the same browser, device, or customer session for analytics attribution. |
| `steps` | The answer history that defines the current assistant state. |
| `next_question_handle` | The next-question hint returned by the selected option. Send it back when present. |
| Last response | Useful for rendering the current question, current hits, and optional back/reset behavior. |

### The `steps` array

`steps` is an array of objects, where each object represents one answered question: which question it was and which option or options the shopper selected.

| Field | Required | Description |
| :-- | :-- | :-- |
| `question_handle` | Yes | The handle of the question the shopper answered. |
| `option_handles` | Yes | The selected option handles. One value for a single-choice question, multiple values for a multi-choice question. |

For the first request, the shopper has not selected anything yet, so `steps` is empty:

```json
{
  "steps": []
}
```

After the shopper answers the first question:

```json
{
  "steps": [
    {
      "question_handle": "budget",
      "option_handles": ["budget-low"]
    }
  ]
}
```

Each new selection appends another object. Always send the full array, not only the latest selection. The API needs the complete history to calculate the correct question and product set:

```json
{
  "steps": [
    {
      "question_handle": "budget",
      "option_handles": ["budget-low"]
    },
    {
      "question_handle": "use_case",
      "option_handles": ["home-use", "travel"]
    }
  ]
}
```

For multi-choice questions, group all selected options for that question into the same step.

### Back and reset

Back and reset are frontend state operations. Modify the `steps` array locally, then call the API again:

- **Back one step:** remove the last entry from `steps`.
- **Jump to a previous step:** slice `steps` to that point.
- **Reset:** clear `steps`.

You can cache previous responses for a smoother UI, but the canonical state is still the `steps` array.

## 2. Making the request

Send a `POST` request to `https://live.luigisbox.com/v1/assistant`. No authentication header is required.

Identify your site and assistant using query parameters. Pass your state and formatting preferences in the JSON body:

```shell
curl -X POST "https://live.luigisbox.com/v1/assistant?tracker_id=YOUR_TRACKER_ID&assistant_handle=YOUR_ASSISTANT&user_id=USER_123" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_version": -1,
    "steps": [],
    "hit_fields": "title,price_amount,image_link"
  }'
```

Key request body fields:

| Field | Description |
| :-- | :-- |
| `assistant_version` | Use `-1` to resolve the latest published version, or a specific version number for deterministic behavior. Must be an integer — not `null`, an empty string, or a quoted value. |
| `steps` | Pass an empty array `[]` for the initial request, then the full history thereafter. |
| `next_question_handle` | When the selected option returns this, pass it in your next request. If the handle is invalid, the request returns `400`. If it is omitted when the assistant expects explicit branching, the flow may not advance to the next question. |
| `hit_fields` | Comma-separated list of product attributes to return. Reduces payload size. |
| `size` | Number of hits per response. Default `10`, maximum `200`. There is no pagination — the assistant narrows results through questions. |
| `price_field` | Numeric catalog field used to calculate option price ranges. Defaults to `price_amount`. |
| `field_overrides` | An object that replaces fields used by assistant filters and facets with other indexed fields at request time. Use it when the assistant is configured against a generic field, but the actual field depends on the current user or request. Invalid mappings return `400`. |
| `f`, `f_must`, `neg_f`, `neg_f_must` | External filters from your page or business logic. Values use `field:value` syntax, for example `brand:Nike`. See [Filters and facets](/search/guides/filters-and-facets/) for the full syntax. |

For the complete parameter list, see the [API reference](/shopping-assistant/api/assistant/#request-parameters).

## 3. Handling the response

The API returns two surfaces: `question` (the next question to ask) and `hits` (the products matching the current selection). See the [HTTP response reference](/shopping-assistant/api/assistant/#http-response) for the full field list.

### Question

Render `title_html`, `description_html`, and each option's display fields. Sanitize HTML before injecting it into the DOM — the content is authored in the Luigi's Box app, but any external HTML is a security risk.

Each option inside `question.options` includes:

| Field | Description |
| :-- | :-- |
| `option_handle` | Identifier used in `steps` when the shopper selects this option. |
| `title_html`, `description_html` | Display content. |
| `hits_count` | Products that match if this option is selected. |
| `price_range` | Price range of the matching products. |
| `next_question_handle` | Optional in the response shape. If present, send it back exactly in the next request. |

### Hits

The `hits` array contains products matching the current `steps` history. Render the fields you requested through `hit_fields`.

Each hit also carries a top-level `url` — the [object identity](/platform-foundations/identity/). Pass this exact value when sending click and add-to-cart events so Luigi's Box pairs them to the correct product. `hit_fields` filters the `attributes` object only; top-level fields like `url` are always returned.

If `hits` is empty while questions are still active, no products match the current selection. Show an empty state and let the shopper go back.

## 4. Advancing the flow

When a shopper selects an option:

1. Append their selection to your local `steps` array.
2. Check the selected option for `next_question_handle`. If present, include it in your next request body and treat it as required for the next call.
3. Call the API with the updated payload.

Disable the option buttons or show a loading state while the request is in flight to prevent race conditions in your `steps` array.

### Completion

When the API returns `question: null`, the dialogue is over. Render the current `hits` as the final recommendation set and give the shopper a way to go back or start again.

If you expected another question, first check whether the selected option returned `next_question_handle` and whether your next request passed it through. When the flow uses explicit branching, omitting it can result in `question: null`.

## Error handling

If the API request fails, do not mutate your local `steps` state. Display an error message and allow the shopper to retry.

| Status | What to do |
| :-- | :-- |
| `400` | A parameter is invalid, or the assistant handle or version cannot be resolved. Check `tracker_id`, `assistant_handle`, `assistant_version`, and `steps` for typos or malformed values. Do not retry automatically; fix the payload. |
| `500` | Temporary server error. Retry with exponential backoff. Log the `Request-Id` header if the response includes one. |

For the full error format, see the [API error reference](/shopping-assistant/api/assistant/#errors).

## Launch checklist

Before taking the integration live, verify:

- A stable `user_id` persists across the entire flow.
- The full `steps` array is sent on every request, not just the latest selection.
- Multi-choice selections are grouped into a single step object.
- HTML fields are sanitized before rendering.
- `hit_fields` includes every product attribute your UI renders.
- Back and reset produce the expected API state.
- Product clicks and add-to-cart actions trigger the appropriate analytics events. See [Shopping Assistant analytics](/shopping-assistant/guides/analytics/).

## Related pages

- [Building a custom shopping assistant](/quickstart/shopping-assistant/building-custom-ui/)
- [Shopping Assistant analytics](/shopping-assistant/guides/analytics/)
- [Shopping Assistant API reference](/shopping-assistant/api/assistant/)
