Shopping Assistant API

Use the Shopping Assistant API endpoint to design interactive, guided product discovery experiences tailored for your users.

This API endpoint navigates structured dialogue flows you have already created. To get started first create an assistant in the Luigi's Box app.

Luigi's Box Assistant can learn from user interactions to provide better recommendations. To enable learning, integrate Luigi's Box Search Analytics service with your website by following the instructions.

The assistant endpoint is publicly available and requires no authentication.

Endpoint

POST https://live.luigisbox.com/v1/assistant

Request

Query parameters

   
tracker_id Your site identifier within Luigi's Box. You can find this identifier in every URL in the Luigi's Box app once you are logged in.
assistant_handle The unique handler of the assistant to use.
user_id The unique identifier of the end-user. If it matches the user ID collected in analytics, it can drive personalization of the assistant results.

Request body parameters

   
assistant_version REQUIRED integer The version of the assistant. Use -1 for the latest version.
next_question_handleoptional string The handle of the next question to be presented to the user. If omitted, the API determines the next question automatically.
price_fieldoptional string The product field used for calculating price ranges. Defaults to price_amount. If you want to use a different field, specify it here. If the field does not exist or is not numeric, an error will be returned.
stepsoptional array[object] An array of previous interactions (questions and answers) in the dialogue. Each step contains the question handle and the selected option handle.
sizeoptional integer The number of product results to return.
sortoptional string Sorting criteria for the product results (e.g., price:asc, price:desc,title:asc, etc.). If not provided, the default sorting will be applied.
contextoptional object Override fields used by search. The fields available for overriding are availability_field, availability_rank_field, freshness_field, boost_field, geo_location_field, margin_field, absolute_margin_field, and discount_field.
foptional array[string] An array of OR filters to apply to the product results (e.g., category:electronics, price:1|5, etc.).
f_mustoptional array[string] An array of AND filters that must match the product results (e.g., category:electronics, price:1|5, etc.).
neg_foptional array[string] An array of OR filters to exclude product results (e.g., category:electronics, price:1|5, etc.).
neg_f_mustoptional array[string] An array of AND filters to exclude the product results (e.g., category:electronics, price:1|5, etc.).
search_in_variantsoptional boolean A boolean value indicating whether to search in product variants. If set to true, the search will include variants of products. Defaults to the search setting set in the app.
non_collapsed_variantsoptional boolean A boolean value indicating whether to return non-collapsed variants in the results. If set to true, variants will be returned as separate items in the results. Works only when search_in_variants is allowed.

Request headers

Consider sending request header of Accept-Encoding as well with values for supported encoding methods of your HTTP client, e.g. gzip or br, gzip, deflate for multiple supported methods. Encodings make the response from the JSON API considerably smaller and thus faster to transfer.

Example request

require 'faraday'
require 'faraday_middleware'
require 'json'

connection = Faraday.new(url: 'https://live.luigisbox.com') do |conn|
  conn.use FaradayMiddleware::Gzip
end

response = connection.post("/v1/assistant?tracker_id=1234-5678&assistant_handle=piano_finder&user_id=123456") do |req|
  req.headers['Content-Type'] = "application/json; charset=utf-8"
  req.body = '{
  "assistant_version": 1,
  "steps": [
    {
      "question_handle": "color",
      "option_handles": [
        "color-blue"
      ]
    }
  ]
}'
end

if response.success?
  puts JSON.pretty_generate(JSON.parse(response.body))
else
  puts "Error, HTTP status #{response.status}"
  puts response.body
end

#!/bin/bash

curl -i -XPOST --compressed\
  "https://live.luigisbox.com/v1/assistant?tracker_id=1234-5678&assistant_handle=piano_finder&user_id=123456"\

   -d '{
  "assistant_version": 1,
  "steps": [
    {
      "question_handle": "color",
      "option_handles": [
        "color-blue"
      ]
    }
  ]
}'

<?php

// Using Guzzle (http://guzzle.readthedocs.io/en/latest/overview.html#installation)
require 'GuzzleHttp/autoload.php';


$client = new GuzzleHttp\Client();
$res = $client->request('POST', "https://live.luigisbox.com/v1/assistant?tracker_id=1234-5678&assistant_handle=piano_finder&user_id=123456", [
  'headers' => [
    'Accept-Encoding' => 'gzip, deflate',
    'Content-Type' => 'application/json; charset=utf-8',
  ],
  'body' => '{
  "assistant_version": 1,
  "steps": [
    {
      "question_handle": "color",
      "option_handles": [
        "color-blue"
      ]
    }
  ]
}'
]);

echo $res->getStatusCode();
echo $res->getBody();

// This endpoint requires no authentication

// Example request body

{
  "assistant_version": 1,
  "steps": [
    {
      "question_handle": "color",
      "option_handles": [
        "color-blue"
      ]
    }
  ]
}

HTTP response

The response to an assistant request is a structured JSON.

Success response (200 OK)

A successful response includes the current state of the dialogue, such as the next question, and a filtered list of products.

Response fields

   
assistant_handle The handle of the assistant being used.
tracker_id Your site identifier within Luigi's Box.
hits An array of product results that match the current state of the dialogue.
important_attributes An array of important attributes that should be displayed for each product in the results.
avatar_image_link The URL to an avatar image for the assistant, if available.
question.title_html An HTML-formatted title of the current question.
question.description_html An HTML-formatted description of the current question, if available.
question.image_link The URL to an image related to the current question, if available.
question.type The type of the question, which can be single_choice or multi_choice.
question.handle The unique identifier for the current question.
question.options[].title_html An HTML-formatted title of the option.
question.options[].description_html A HTML-formatted description of the option, if available.
question.options[].option_handle The unique identifier for the option.
question.options[].next_question_handle The handle of the next question to be presented if this option is selected.
question.options[].image_link The URL to an image related to the option, if available.
question.options[].color_code The color code associated with the option, if applicable.
question.options[].hits_count The number of products that match this option.
question.options[].price_range The price range of products matching this option, formatted as "min - max".

Example success response

{
  "assistant_handle": "piano_finder",
  "tracker_id": "1234-5678",
  "hits": [
    {
      "id": "product_1",
      "title": "Grand Piano",
      "price_amount": 5000,
      "image_link": "https://example.com/images/grand_piano.jpg",
      "important_attributes": ["brand", "model"],
      "updated_at": "2024-10-01T12:00:00Z"
    }
  ],
  "important_attributes": ["brand", "model"],
  "avatar_image_link": "https://example.com/images/avatar.png",
  "question": {
    "title_html": "<strong>What type of piano are you looking for?</strong>",
    "description_html": "<p>Please select one of the options below.</p>",
    "image_link": "https://example.com/images/question_image.jpg",
    "type": "single_choice",
    "handle": "piano_type",
    "options": [
      {
        "title_html": "<strong>Grand Piano</strong>",
        "description_html": "<p>A large and elegant piano.</p>",
        "option_handle": "grand_piano",
        "next_question_handle": null,
        "image_link": null,
        "color_code": null,
        "hits_count": 10,
        "price_range": "$4000 - $6000"
      }
    ]
  }
}

Error Responses

The API uses standard HTTP status codes. Note that the format of the error response body can vary depending on the type of error.

Client-side errors

These errors indicate a problem with the request that was sent.

  • 400 Bad Request: Indicates malformed input, such as a missing parameter or incorrect data type. The response is a structured JSON object.
{
  "type": "malformed_input",
  "reason": "incorrect parameters provided",
  "caused_by": {
    "assistant_version": ["must be an integer"]
  }
}
  • 404 Not Found: Indicates that the requested assistant or question does not exist.
[
  "Assistant with id 'Webnar Guitars' for tracker_id 'YOUR_TRACKER_ID' not found"
]

Server-side errors

These indicate a temporary problem with the service. You should retry the request after a short delay. If the problem persists, contact support.

Status code Response body (text/plain) Reason
408 Request Timeout Request timed out The request took too long to process.
500 Internal Server Error Internal server error Request-Id: ... Indicates a generic server error.
503 Service Unavailable Service Unavailable A backend service is temporarily down. Please retry after a short delay.

Integration guide

Building a structured dialogue flow

The Assistant API is designed to create a structured dialogue-based product discovery experience. Here's how to implement a basic flow:

  1. Start the structured dialogue: Make an initial request with only the tracker_id, assistant_handle, and assistant_version parameters.
  2. Present options: Display the question and options returned in the response to the user.
  3. Track selections: When a user selects an option, add this interaction to the steps array in your next request.
  4. Show results: Display the products returned in the hits array at each stage of the structured dialogue.
  5. Continue the structured dialogue: The API will automatically determine the next most relevant question based on previous answers, or you can specify the next question using next_question_handle.

Example structured dialogue flow

const TRACKER_ID = 'YOUR_TRACKER_ID'; 
const ASSISTANT_HANDLE = 'Webinar Guitars';
const ASSISTANT_VERSION = -1;
const API_ENDPOINT = 'https://live.luigisbox.com/v1/assistant';
const USER_ID = 'some_user_id';

let steps = []; // This array will store the history of the structured dialogue.

// Initial API call to start the structured dialogue
async function startConversation() {
    const url = `${API_ENDPOINT}?tracker_id=${TRACKER_ID}&assistant_handle=${ASSISTANT_HANDLE}&user_id=${USER_ID}`;
    const payload = {
        assistant_version: ASSISTANT_VERSION,
        steps: [] // Sending an empty array
    };

    const response = await axios.post(url, payload);
    renderQuestion(response.data); 
}

function handleOptionClick(questionHandle, selectedOption) {
    // 1. Record the answer by adding it to our state array
    steps.push({
        question_handle: questionHandle,
        option_handles: [selectedOption.option_handle] // API expects an array
    });

    // 2. Get the handle for the next question from the selected option
    const nextQuestionHandle = selectedOption.next_question_handle;

    // 3. Call the API again with the updated steps and the next question handle
    callAssistantAPI(nextQuestionHandle);
}

async function callAssistantAPI(nextQuestionHandle = null) {
    const url = `${API_ENDPOINT}?tracker_id=${TRACKER_ID}&assistant_handle=${ASSISTANT_HANDLE}&user_id=${USER_ID}`;
    const payload = {
        assistant_version: ASSISTANT_VERSION,
        steps: steps // The steps array now contains the user's history
    };

    // Add the handle to the payload to continue the structured dialogue flow
    if (nextQuestionHandle) {
        payload.next_question_handle = nextQuestionHandle;
    }

    const response = await axios.post(url, payload);
    renderQuestion(response.data);
}

function renderQuestion(data) {
    if (data.question && data.question.options.length > 0) {
        // Render the question and options...
    } else {
        // No more questions, the structured dialogue is over.
        displayCompletionMessage();
    }
}

Price range display

The Assistant API automatically calculates price ranges for each option, helping users understand the price distribution of products that match each option. This is calculated using the field specified in the price_field parameter (defaults to price_amount).

Analytics and performance tracking

Once your assistant is live, you can measure its performance and understand user behavior through the Shopping Assistant Analytics dashboard in the Luigi's Box app.

How analytics are collected

Luigi's Box tracks analytics in two ways: automatically through its servers, and manually via events you set up.

What's tracked automatically:

Every API call you make is logged. This means Luigi's Box automatically tracks the entire structured dialogue flow: which questions were shown, which answers were selected, and which products were displayed in the hits array (this is the "Assistant Listing" event). This powers metrics like Activation Rate and Completion Rate.

What you must track manually:

To measure the assistant's business impact, you must manually report what users do after the products are displayed. To enable key metrics like Conversion Rate (CVR) and Click-Through Rate (CTR), you must send events when a user acts on a product shown by the assistant:

  • Clicks: Report a click event when a user clicks on a product card.

  • Conversions: Report a conversion event (e.g., "add to cart") when a user takes a conversion action on a product.

Key metrics you can track

By ensuring full event tracking is in place, you can unlock powerful insights in your analytics dashboard, including:

  • Activation Rate: The percentage of users who start a dialogue after seeing the the teaser (e.g., the button or link that opens the assistant).
    • High: The teaser is engaging and relevant; keep the style, test small variations to push higher.
    • Low: The teaser isn't catching attention; refresh copy/visuals, adjust placement, or better align the teaser promise with assistant value.
    • Diagnostic tip: If Activation is low and CVR is low, the teaser likely isn't attracting the right audience. If Activation is high but CVR is low, the teaser may be over-promising.
  • Completion Rate: The percentage of users who reach the final recommendation step after starting a dialogue.
    • High: The question flow is engaging and easy to follow; keep the style, test small variations to push higher.
    • Low: The question flow may be confusing or too long; consider simplifying the questions, reducing the number of steps, or improving the clarity of options.
    • Diagnostic tip: A sudden drop in Completion with stable Activation often points to a new friction point in the flow.
  • Click-Through Rate (CTR): The percentage of users who click on a product after it is displayed by the assistant.
    • High: The products shown are relevant and appealing; keep the style, test small variations to push higher.
    • Low: The products may not be relevant or appealing; consider improving product relevance, enhancing product images/descriptions, or revising the question flow to better match user intent.
    • Diagnostic tip: If Completion Rate is high but both CTR and CVR are low, it's a strong signal that the final product recommendations are the problem. The user trusted the flow but didn't like the results.
  • Conversion Rate (CVR): The percentage of users who add a product to their cart or purchase it after using the assistant.
    • High: The assistant is effectively guiding users to products they want; keep the style, test small variations to push higher.
    • Low: The assistant may not be effectively matching users with products; consider revising the question flow, improving product relevance, or enhancing the user experience.
    • Diagnostic tip: If CVR is low but Completion is high, the recommendation step may not be persuasive or relevant enough.
  • Exit Rate per Question: See which questions cause users to abandon the dialogue, helping you identify confusing or unhelpful steps.
    • High: A specific question may be causing confusion or frustration; consider revising or removing it.
    • Low: The questions are engaging and relevant; keep the style, test small variations to push higher.
    • Diagnostic tip: A spike in exits at a single question is a strong signal to re-evaluate that step's wording, complexity, or necessity.

By monitoring these metrics in the Luigi's Box app, you can continuously refine your assistant's questions and flow to better guide users and improve performance.

Best practices

Design effective questions

Create questions that help users narrow down their choices effectively. Each question should:

  • Address a specific aspect of the product selection process.
  • Have options that meaningfully divide the product catalog.
  • Present clear, concise option text.

Track user journeys

Analyze how users navigate through your assistant to optimize the question flow and product recommendations. Use the analytics integration to:

  • Identify common paths through the assistant.
  • Detect questions where users frequently abandon the process.
  • Measure conversion rates from assistant interactions.

Show relevant results early

While the assistant is designed to guide users to the perfect product through a series of questions, showing relevant results early in the process can improve user satisfaction. The API returns matching products at each step, so consider:

  • Displaying a selection of top matches alongside questions.
  • Highlighting the diversity of available options.
  • Showing how each answer changes the result set.

Ensure mobile compatibility

Design your assistant interface to work well on mobile devices, where users may prefer a guided experience over traditional filtering.