--- title: Collections description: Build category, brand and landing pages with POST /discovery/v1/collections — a ranked listing bounded by a scope instead of a query. slug: discovery/collections docKind: guide hub: luigisbox-ai --- 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 supplies a **scope** and the surface ranks whatever falls inside it. `POST /discovery/v1/collections` is the endpoint. It behaves like [search](/discovery/search/) with one field swapped: `collection_filters` instead of `query`. :::caution[A change to this endpoint is already scheduled] Beyond the pre-launch notice above: the scope is moving from `collection_filters` onto a first-class collection resource in an upcoming release, which reshapes the request body. Read this page to understand how collections work, and build against it if you need to — but expect to rework the request, and tell your Luigi's Box contact first so we can give you the new shape ahead of the release rather than after it. ::: ## Request ```bash curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/collections?channel_id=lbn_4hj9tv&surface_id=lbs_collection_category_pages' \ -H 'Authorization: Bearer ' \ -H 'X-Lbx-Visitor-Id: 8f14e45f-ea0f-4b5c-9a1d-2b3c4d5e6f70' \ -H 'Content-Type: application/json' \ -d '{ "type": "product", "collection_filters": { "@category": { "in": ["category/shirts"] } }, "size": 24 }' ``` | Query parameter | Required | Notes | |---|---|---| | `channel_id` | Yes | Serving destination | | `surface_id` | Yes | Must be a `collection` surface | | Body field | Required | Default | Notes | |---|---|---|---| | `type` | Yes | — | Usually `product` | | `collection_filters` | No | the surface's whole scope | The page's scope, as a [filter tree](/discovery/filters/) | | `filters` | No | none | The shopper's own narrowing, on top of the scope | | `size` | No | `10` | 1–100 | | `sort` | No | relevance | `{ "field": …, "direction": "asc" \| "desc" }` — see [sorting](/discovery/sorting-and-pagination/#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](/discovery/fields/) | | `user_id` / `personalize` | No | anonymous / `true` | [Personalization](/discovery/personalization/) | The response is identical in shape to search: `hits`, `total`, `total_approx`, `guid`, `next_page_cursor`, `pagination_status`. See [Anatomy of a response](/discovery/overview/#anatomy-of-a-response). Omitting `collection_filters` browses everything the surface is configured to serve, which suits an "all products" page; on a category page every category would render the same listing. ## Scope versus filters Both fields take a [filter tree](/discovery/filters/). | | `collection_filters` | `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 | ```json { "collection_filters": { "@category": { "in": ["category/shirts"] } }, "filters": { "color": { "eq": "blue" }, "price": { "lte": 50 } } } ``` Merging the two into one tree returns the same products, but 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*. ## Common scopes ```jsonc // One category { "@category": { "in": ["category/shirts"] } } // A category and its siblings { "@category": { "in": ["category/shirts", "category/knitwear"] } } // Everything from a brand { "@brand": { "eq": "brand/northwear" } } // A curated edit, driven by a catalog attribute you control { "collection": { "eq": "summer-sale-2026" } } ``` In the last pattern, a plain catalog attribute defines a merchandised page. Tag products with `collection: "summer-sale-2026"` in your feed and the landing page needs no further configuration. ## Faceting a collection Same as search — take the `guid` from the response and ask [`/facets`](/discovery/facets/) for the filters: ```bash curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/facets?channel_id=lbn_4hj9tv' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "guid": "3ab549eb64a20a0c", "facets": [ { "field": "brand", "size": 20 }, { "field": "color" }, { "field": "price" } ] }' ``` The counts reflect the scope as well as any `filters`, so a collection page's facets never offer a value that would return nothing. ## Sorting Listing pages commonly offer explicit sorting: ```json { "sort": { "field": "price", "direction": "asc" } } ``` With no `sort`, the surface's own ranking applies; use it as the default option in the sort dropdown. See [Sorting and pagination](/discovery/sorting-and-pagination/). ## 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](/analytics/references/), which is what credits the activity to this page. See [Sending events](/analytics/sending-events/). ## See also - [Filters](/discovery/filters/) — the grammar for both scope and filters - [Facets](/discovery/facets/) · [Sorting and pagination](/discovery/sorting-and-pagination/) - [Object types and relationships](/concepts/object-types/) — how `@category` and `@brand` work - [Business rules](/merchandising/business-rules/overview/) — pinning a hero product on a collection