Skip to content

    Pre-launch API. The concepts described here are settled, but the API shape is not: request and response fields, parameters and defaults can still change. Build against it, and talk to your Luigi's Box contact before you put an integration into production.

    Personalization

    View source

    Personalization tailors a slate to the shopper in front of it, using what they have done recently in this catalog, across the channels that serve it. It is on by default and turned off per request with "personalize": false.

    InputWhereIdentifies
    X-Lbx-Visitor-IdHeader, required on every discovery requestThis browser, over the long term
    user_idBody field, optionalThe signed-in shopper, across their devices
    personalizeBody field, default trueWhether to personalize this request
    Terminal window
    curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/search?channel_id=lbn_4hj9tv&surface_id=lbs_search_main' \
    -H 'Authorization: Bearer <token>' \
    -H 'X-Lbx-Visitor-Id: 8f14e45f-ea0f-4b5c-9a1d-2b3c4d5e6f70' \
    -H 'Content-Type: application/json' \
    -d '{
    "type": "product",
    "touchpoint": "serp",
    "query": "running shoes",
    "personalize": true,
    "user_id": "customer-88213"
    }'

    personalize asserts two things at once: personalize this answer, and this shopper has consented to behavioural personalization. The flag is recorded with the request, so it governs both what the shopper sees and what the request may be used for afterwards.

    Read it from your consent manager on every request rather than relying on the default:

    // Read consent from your own consent manager — never hardcode it.
    const personalize = consentManager.hasAnalyticsConsent();

    The corresponding flag on analytics events is consent_granted. Read both from the same source, so a shopper’s choice is applied consistently on the request and on the event that reports it. See Sending events.

    X-Lbx-Visitor-Id is required on every discovery request whether or not you personalize, and it is the input personalization depends on most.

    • Generate it once per browser and persist it — a UUID in a first-party cookie or localStorage.
    • Keep it stable across token refreshes, page loads and sessions. A visitor ID that changes per page load makes every visit look like a new shopper. A personalized result set belongs to the visitor it was ranked for, so a visitor ID that changes between pages also invalidates the cursor with a 422.
    • Never generate it server-side. If your backend calls discovery, your frontend produces the value and your backend forwards it verbatim. A server-generated ID collapses every shopper into one.
    • Send a real value. undefined, null, None, NaN or [object Object], what a template produces for a missing variable, identify nobody: unless the shopper is signed in and sends user_id, results for such a request are not personalized.
    • Reset it on sign-out if a shared device is a realistic concern for your storefront.
    function visitorId() {
    let id = localStorage.getItem('lbx_visitor_id');
    if (!id) {
    id = crypto.randomUUID();
    localStorage.setItem('lbx_visitor_id', id);
    }
    return id;
    }

    user_id is your own stable customer key. Sending it lets a signed-in shopper’s history follow them between devices — the phone they browsed on and the laptop they buy on.

    • Send it only while the shopper is authenticated in your storefront.
    • Use a stable internal key, not an email address or anything else that identifies a person. Luigi’s Box treats it as an opaque string; you decide what it reveals.
    • Omit it, or send it blank, for anonymous traffic. Both are treated as anonymous, and so are the placeholders a missing value tends to turn into: undefined, null, None, NaN and [object Object], in any case.
    • Keep sending X-Lbx-Visitor-Id as well. The two are complementary, not alternatives.

    Personalization is built from what a shopper chose, reported through analytics events:

    CountsReported as
    Clicks on a productinteraction, with interaction_type: "click"
    Cart additions, and any other conversion you reportinteraction, with another interaction_type
    Purchasestransaction

    Seeing a product does not count, whether in a list or on its detail page. The shopper’s most recent choices are what counts: each result is lifted by how closely it resembles the most similar of them, so a shopper who bought running shoes and clicked on tents sees both kinds lifted, each where it fits. A product the shopper already chose is lifted the most, and where repeat purchases matter a surface can lift it — or a variant of it — further still; that extra lift is off unless the surface sets it. On catalogs where shoppers rarely buy the same thing twice, a surface can instead lift only products the shopper has not chosen yet. The two settings do not combine.

    Those events need consent_granted: true, and the shopper’s X-Lbx-Visitor-Id — or user_id when signed in — to match the one on the discovery request, or the two cannot be connected.

    One chosen product is enough for results to change. Before that first choice, results are the same as for a first-time visitor. Choosing the same product repeatedly counts once, not once per time.

    Personalization helps most where a shopper’s intent is broad and the catalog is large, and least where they have already been specific.

    PlacementPersonalize?Why
    Homepage recommendationsYesNothing else to go on
    Search with a broad query — “shoes”YesMany valid answers; recent behaviour breaks the tie
    Search with a precise query — a SKUNo effectOne right answer already
    Category listingOftenOrdering within a large category
    Cart cross-sellYesThe cart plus history beats the cart alone
    Explicit sort — “price, low to high”No effect on orderThe shopper asked for a specific ordering

    Send true wherever you have consent; the surface decides how much to use.

    Personalized and non-personalized traffic are reported separately; compare them, or run an A/B test on the surface, to size the effect. Both need analytics events flowing: without the clicks, cart additions and purchases described above there is nothing to personalize from, and nothing to measure with.