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.
A collection is a listing page: everything in a category, everything from a brand, everything in a seasonal edit. There is no query; the request names a collection and the surface ranks what belongs to it.
POST /discovery/v1/collection is the endpoint. It behaves like search
with one field swapped: collection_id instead of query. The endpoint used to be
POST /discovery/v1/collections; that path still answers, with the same body, but is no
longer part of the published contract, so move to the singular one.
A membership or curated collection has to be registered on the catalog before a request can
name it; see Collections. An all collection,
collection:all/member:{member_type}, needs no registration.
A new catalog comes with one collection surface, lbs_collection_default, which serves
products; see Catalogs.
Request
Section titled “Request”curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/collection?channel_id=lbn_4hj9tv&surface_id=lbs_collection_default' \ -H 'Authorization: Bearer <token>' \ -H 'X-Lbx-Visitor-Id: 8f14e45f-ea0f-4b5c-9a1d-2b3c4d5e6f70' \ -H 'Content-Type: application/json' \ -d '{ "collection_id": "collection:category/shirts/member:product", "touchpoint": "plp", "size": 24 }'| Query parameter | Required | Notes |
|---|---|---|
channel_id | Yes | Serving destination |
surface_id | Yes | Must be a collection surface |
| Body field | Required | Default | Notes |
|---|---|---|---|
collection_id | Yes | — | The collection to list, see Collection IDs |
touchpoint | No | none | Where the listing appears. One of plp, landing_page, homepage, pdp, chat, mcp_agent — see Touchpoints |
filters | No | none | The shopper’s own narrowing, on top of the collection |
size | No | 10 | 1–100 |
sort | No | relevance | { "field": …, "direction": "asc" | "desc" } — see sorting |
cursor | No | first page | From a previous next_page_cursor. Omit it for the first page; an empty string is a 422. |
return_fields | No | surface default | Field projection |
user_id / personalize | No | anonymous / true | Personalization |
The body takes no type: the collection ID names the type it returns, and the surface must
serve that type. Bodies built before collection IDs send type and collection_filters
instead, see Deprecated: type and collection_filters.
The response is identical in shape to search: hits, total, total_approx,
request_key (and its deprecated alias guid), next_page_cursor,
pagination_status. See
Anatomy of a response.
Collection IDs
Section titled “Collection IDs”A membership collection is listed one object at a time. The ID is the object’s @id and the
type the listing returns:
// Products in the shirts category"collection:category/shirts/member:product"
// Products from a brand"collection:brand/northwear/member:product"The listing holds the member objects whose membership attribute contains the @id. For a
parent category to list its subcategories’ products too, the attribute on each product has to
carry every category above it as well as its own, as lbx:category_ids does.
A curated collection is named by its own ID:
"collection:curated/new_arrivals/member:product"The listing holds the member objects matching the curated collection’s filters.
Every object of one type is listed by the type’s all collection, which needs no registration:
// All categories"collection:all/member:category"| Response | When |
|---|---|
404, error_code collection.not_found | The curated collection is not registered, or the @id is not in the catalog |
404, error_code collection.type_not_registered | No membership collection is registered for the type and member type |
422 | The ID is malformed, names a membership collection’s registration rather than one of its objects, or the surface does not serve the member type |
The error_code is under exception_details.
Collection versus filters
Section titled “Collection versus filters”collection_id | filters | |
|---|---|---|
| Owned by | The page | The shopper |
| Means | ”This page is the shirts category" | "…and I only want blue ones under €50” |
| Changes when | The shopper navigates to another page | The shopper clicks a facet |
{ "collection_id": "collection:category/shirts/member:product", "filters": { "color": { "eq": "blue" }, "price": { "lte": 50 } }}Facet counts, “clear all filters” and merchandising rules scoped to a collection depend on the distinction between what the page is and what the shopper asked for.
Faceting a collection
Section titled “Faceting a collection”Same as search — take the request_key from the response and ask
/facets
for the filters:
curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/facets?channel_id=lbn_4hj9tv' \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "request_key": "3ab549eb64a20a0c", "facets": [ { "field": "brand", "size": 20 }, { "field": "color" }, { "field": "price" } ] }'The counts reflect the collection as well as any filters, and any product a pin placed, so a
collection page’s facets never offer a value that would return nothing.
Sorting
Section titled “Sorting”Listing pages commonly offer explicit sorting:
{ "sort": { "field": "price", "direction": "asc" } }With no sort, the surface’s own ranking applies; use it as the default option in the sort
dropdown. A collection surface can favour available products, popular products, or other
configured attributes. These boosts change ranking without removing products from the
collection. An explicit sort takes precedence. See
Sorting and pagination.
Reporting
Section titled “Reporting”The listing itself is recorded when the request is served. Report what the shopper does with it — clicks, cart additions, purchases. Each event names the object with a reference, which is what credits the activity to this page. See Sending events.
Deprecated: type and collection_filters
Section titled “Deprecated: type and collection_filters”A body can name the type and bound the listing with a filter tree of its own instead of a collection ID:
{ "type": "product", "collection_filters": { "@category": { "in": ["category/shirts"] } }, "filters": { "color": { "eq": "blue" } }}Such a request is served as it was before collection IDs: nothing has to be registered, the
listing holds the objects of type matching collection_filters, and omitting
collection_filters lists everything the surface serves. For
campaign triggers, a collection_filters that is
an AND of equality or membership conditions exposes each attribute as written, e.g.
trigger.@category; one using OR, NOT or a range exposes nothing.
collection_filters takes type, and neither can be sent with collection_id: such a body is
a 422. Move to collection_id, as these fields will be removed.
See also
Section titled “See also”- Collections — registering what a request can name
- Filters — the grammar of
filters - Facets · Sorting and pagination
- Object types and relationships — how
@categoryand@brandwork - Business rules — pinning a hero product on a collection
Was this page helpful?
Thanks.