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.

    Collection

    View source

    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.

    Terminal window
    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 parameterRequiredNotes
    channel_idYesServing destination
    surface_idYesMust be a collection surface
    Body fieldRequiredDefaultNotes
    collection_idYes—The collection to list, see Collection IDs
    touchpointNononeWhere the listing appears. One of plp, landing_page, homepage, pdp, chat, mcp_agent — see Touchpoints
    filtersNononeThe shopper’s own narrowing, on top of the collection
    sizeNo101–100
    sortNorelevance{ "field": …, "direction": "asc" | "desc" } — see sorting
    cursorNofirst pageFrom a previous next_page_cursor. Omit it for the first page; an empty string is a 422.
    return_fieldsNosurface defaultField projection
    user_id / personalizeNoanonymous / truePersonalization

    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.

    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"
    ResponseWhen
    404, error_code collection.not_foundThe curated collection is not registered, or the @id is not in the catalog
    404, error_code collection.type_not_registeredNo membership collection is registered for the type and member type
    422The 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_idfilters
    Owned byThe pageThe shopper
    Means”This page is the shirts category""…and I only want blue ones under €50”
    Changes whenThe shopper navigates to another pageThe 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.

    Same as search — take the request_key from the response and ask /facets for the filters:

    Terminal window
    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.

    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.

    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.

    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.