--- title: Personalization description: How personalize, user_id and the visitor identifier work together to tailor discovery results — and what consent means on a discovery request. slug: discovery/personalization docKind: guide hub: luigisbox-ai --- Personalization tailors a slate to the shopper in front of it, using what they have done recently in this channel. It is on by default and turned off per request with `"personalize": false`. ## The three inputs | Input | Where | Identifies | |---|---|---| | `X-Lbx-Visitor-Id` | Header, **required** on every discovery request | This browser, over the long term | | `user_id` | Body field, optional | The signed-in shopper, across their devices | | `personalize` | Body field, default `true` | Whether to personalize *this* request | ```bash curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/search?channel_id=lbn_4hj9tv&surface_id=lbs_search_main' \ -H 'Authorization: Bearer ' \ -H 'X-Lbx-Visitor-Id: 8f14e45f-ea0f-4b5c-9a1d-2b3c4d5e6f70' \ -H 'Content-Type: application/json' \ -d '{ "type": "product", "query": "running shoes", "personalize": true, "user_id": "customer-88213" }' ``` ## `personalize` is also your consent signal `personalize` asserts two things at once: *personalize this answer*, and *this shopper has consented to behavioural personalization*. The flag is recorded on the ranking record for 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: ```js // Read consent from your own consent manager — never hardcode it. const personalize = consentManager.hasAnalyticsConsent(); ``` :::caution[The default assumes consent] An omitted `personalize` is treated like `"personalize": true`, so a request that leaves it out is recorded as consented. Send `"personalize": false` for a shopper who has not consented — that is the only thing that keeps the request out of behavioural personalization. ::: 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](/analytics/sending-events/). ## Identify the browser correctly `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. - **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. - **Reset it on sign-out** if a shared device is a realistic concern for your storefront. ```js function visitorId() { let id = localStorage.getItem('lbx_visitor_id'); if (!id) { id = crypto.randomUUID(); localStorage.setItem('lbx_visitor_id', id); } return id; } ``` ## Adding `user_id` when the shopper signs in `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. - Keep sending `X-Lbx-Visitor-Id` as well. The two are complementary, not alternatives. ## Where to turn it on Personalization helps most where a shopper's intent is broad and the catalog is large, and least where they have already been specific. | Placement | Personalize? | Why | |---|---|---| | Homepage recommendations | Yes | Nothing else to go on | | Search with a broad query — "shoes" | Yes | Many valid answers; recent behaviour breaks the tie | | Search with a precise query — a SKU | Little effect | One right answer already | | Category listing | Often | Ordering within a large category | | Cart cross-sell | Yes | The cart plus history beats the cart alone | | Explicit sort — "price, low to high" | No effect on order | The shopper asked for a specific ordering | Send `true` wherever you have consent; the surface decides how much to use. ## Measuring it 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](/analytics/sending-events/) flowing: without clicks, cart additions and purchases, there is nothing to personalize from and nothing to measure with. ## See also - [Requests and responses](/api-basics/requests-and-responses/#identifying-the-shopper) — visitor and session identifiers - [Sending events](/analytics/sending-events/) — the `consent_granted` flag - [Recommendations](/discovery/recommendations/) · [Search](/discovery/search/) - [Analytics overview](/analytics/overview/)