--- title: Collection description: Build category, brand and landing pages with POST /discovery/v1/collection — a ranked listing of a collection instead of a query. slug: discovery/collection 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 names a **collection** and the surface ranks what belongs to it. `POST /discovery/v1/collection` is the endpoint. It behaves like [search](/discovery/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](/platform/account-structure/#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](/platform/account-structure/#catalogs). ## Request ```bash curl -X POST 'https://api.eu1.luigisbox.ai/discovery/v1/collection?channel_id=lbn_4hj9tv&surface_id=lbs_collection_default' \ -H 'Authorization: Bearer ' \ -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](#collection-ids) | | `touchpoint` | No | none | Where the listing appears. One of `plp`, `landing_page`, `homepage`, `pdp`, `chat`, `mcp_agent` — see [Touchpoints](/concepts/domain-model/#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](/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 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`](#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](/discovery/overview/#anatomy-of-a-response). ## 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: ```jsonc // 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: ```jsonc "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: ```jsonc // 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 | | `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 | ```json { "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 Same as search — take the `request_key` 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 '{ "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 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. 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](/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/). ## Deprecated: `type` and `collection_filters` A body can name the type and bound the listing with a [filter tree](/discovery/filters/) of its own instead of a collection ID: ```json { "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](/merchandising/business-rules/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 - [Collections](/platform/account-structure/#collections) — registering what a request can name - [Filters](/discovery/filters/) — the grammar of `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