# Update a collection `PATCH /platform/v1/catalogs/{catalog_id}/collections/{collection_id}` Replace a curated collection's `filters`. Only `filters` can change; a membership collection cannot be updated and answers `422`. Filter trees are normalised before they are stored — a negative leaf gains an `exists` on its field beside it (see `filters`) — so the resource this returns can differ from the body sent. The response carries the stored form. ## Request example ```bash curl --request PATCH \ --url 'https://api.eu1.luigisbox.ai/platform/v1/catalogs//collections/collection:curated/new_arrivals/member:product' \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "filters": { "$and": [ { "brand": { "in": [ "nike", "adidas" ] } }, { "lbx:price": { "gte": 50, "lte": 200 } }, { "$or": [ { "condition": { "eq": "new" } }, { "warranty_months": { "gte": 12 } } ] }, { "$not": { "material": { "eq": "leather" } } }, { "image": { "exists": true } } ] } }' ``` ## Path Parameters | Name | Type | Required | Description | Constraints | |---|---|---|---|---| | `catalog_id` | `string` | Yes | Catalog Id | — | | `collection_id` | `string` | Yes | Collection id, e.g. `collection:category/member:product`. Its `/` may be sent as is or percent-encoded. | — | ## Request Body **Required.** ### application/json Schema: `object` #### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `filters` | `oneOf(object, object)` | Yes | Filter over the member type, in map syntax, e.g. `{"label": {"eq": "new"}}`. Fields must exist on the member type; `@`-prefixed fields skip that check but not the value check. The `private:`, `signals:`, `ml:` and `lexical:` namespaces are refused. A negative condition (`neq`, `not_in`) is stored with an `exists` on the same field, so it means "has the field, and it differs"; a `$not` over a single positive condition gets the same. `@id`, `@type` and `@updated_at` are exempt. Once stored, at most 100 conditions (each added `exists` included; groups are not counted), in groups nested at most 10 deep, and 65536 bytes, with at most 100 values in one `in` / `not_in` list and integers that fit in 64 bits. A filter past the depth or condition cap is a `validation_errors` entry of type `filter_too_complex`. | — | | `filters.$and` | `Array` | No | Nodes that must all match. | >= 1 items | | `filters.$not` | `oneOf(object, object)` | No | Filter over the member type, in map syntax, e.g. `{"label": {"eq": "new"}}`. Fields must exist on the member type; `@`-prefixed fields skip that check but not the value check. The `private:`, `signals:`, `ml:` and `lexical:` namespaces are refused. A negative condition (`neq`, `not_in`) is stored with an `exists` on the same field, so it means "has the field, and it differs"; a `$not` over a single positive condition gets the same. `@id`, `@type` and `@updated_at` are exempt. Once stored, at most 100 conditions (each added `exists` included; groups are not counted), in groups nested at most 10 deep, and 65536 bytes, with at most 100 values in one `in` / `not_in` list and integers that fit in 64 bits. A filter past the depth or condition cap is a `validation_errors` entry of type `filter_too_complex`. | — | | `filters.$or` | `Array` | No | Nodes of which at least one must match. | >= 1 items | | `filters.*` | `object` | No | Operators applied to one field. Several operators in one object combine as AND. | — | | `filters.*.eq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.exists` | `boolean` | No | — | — | | `filters.*.gt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.gte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.in` | `Array` | No | — | >= 1 items | | `filters.*.lt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.lte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.neq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.not_in` | `Array` | No | — | >= 1 items | #### Request body example ```json { "filters": { "$and": [ { "brand": { "in": [ "nike", "adidas" ] } }, { "lbx:price": { "gte": 50, "lte": 200 } }, { "$or": [ { "condition": { "eq": "new" } }, { "warranty_months": { "gte": 12 } } ] }, { "$not": { "material": { "eq": "leather" } } }, { "image": { "exists": true } } ] } } ``` ## Responses | Status | Description | Content | |---|---|---| | `200` | Successful Response | application/json: oneOf(object, object) | | `400` | Malformed request | application/json: object | | `401` | Missing or invalid credentials | application/json: object | | `403` | Authenticated but not permitted | application/json: object | | `404` | Resource not found | application/json: object | | `422` | Request validation failed | application/json: object | | `503` | The catalog's types could not be read, so the filter could not be checked against them. Retryable: `Retry-After` carries the number of seconds to wait. | application/json: object | ### 200 Successful Response #### application/json Schema: `oneOf(object, object)` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `ancestry_attribute` | `anyOf(string, null)` | Yes | Attribute of the collection type holding the ancestor `@id`s of each collection object, or `null` for a collection type without hierarchy. | — | | `catalog_id` | `string` | Yes | Catalog the collection belongs to. | — | | `collection_type` | `string` | Yes | Catalog object type whose objects are the collections, e.g. `category`. It must exist in the catalog; `curated` and `all` are reserved. | — | | `created_at` | `string` | Yes | When the collection was registered. | format: date-time | | `id` | `string` | Yes | Collection id, built from the registration. A membership collection is `collection:{collection_type}/member:{member_type}`; a request serves one of its objects as `collection:{@id}/member:{member_type}`. A curated collection is `collection:curated/{slug}/member:{member_type}`, and a request serves it by this id. | — | | `kind` | `string` | Yes | `membership`: every object of `collection_type` is a collection of the member objects that reference it through `membership_attribute`. | allowed value: membership | | `member_type` | `string` | Yes | Catalog object type the collection returns. It must exist in the catalog. | — | | `membership_attribute` | `string` | Yes | Attribute of the member type holding the `@id` of every collection object the member belongs to. | — | | `updated_at` | `string` | Yes | When the collection was last changed; its registration time until then. | format: date-time | | `filters` | `oneOf(object, object)` | Yes | Filter over the member type, in map syntax, in its stored form: negations carry the `exists` added on write. | — | | `filters.$and` | `Array` | No | Nodes that must all match. | >= 1 items | | `filters.$and[].*` | `object` | No | Operators applied to one field. Several operators in one object combine as AND. | — | | `filters.$and[].*.eq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.exists` | `boolean` | No | — | — | | `filters.$and[].*.gt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.gte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.in` | `Array` | No | — | >= 1 items | | `filters.$and[].*.lt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.lte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.neq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$and[].*.not_in` | `Array` | No | — | >= 1 items | | `filters.$not` | `oneOf(object, object)` | No | Filter over the member type, in map syntax, e.g. `{"label": {"eq": "new"}}`. Fields must exist on the member type; `@`-prefixed fields skip that check but not the value check. The `private:`, `signals:`, `ml:` and `lexical:` namespaces are refused. A negative condition (`neq`, `not_in`) is stored with an `exists` on the same field, so it means "has the field, and it differs"; a `$not` over a single positive condition gets the same. `@id`, `@type` and `@updated_at` are exempt. Once stored, at most 100 conditions (each added `exists` included; groups are not counted), in groups nested at most 10 deep, and 65536 bytes, with at most 100 values in one `in` / `not_in` list and integers that fit in 64 bits. A filter past the depth or condition cap is a `validation_errors` entry of type `filter_too_complex`. | — | | `filters.$not.*` | `object` | No | Operators applied to one field. Several operators in one object combine as AND. | — | | `filters.$not.*.eq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.exists` | `boolean` | No | — | — | | `filters.$not.*.gt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.gte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.in` | `Array` | No | — | >= 1 items | | `filters.$not.*.lt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.lte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.neq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$not.*.not_in` | `Array` | No | — | >= 1 items | | `filters.$or` | `Array` | No | Nodes of which at least one must match. | >= 1 items | | `filters.$or[].*` | `object` | No | Operators applied to one field. Several operators in one object combine as AND. | — | | `filters.$or[].*.eq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.exists` | `boolean` | No | — | — | | `filters.$or[].*.gt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.gte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.in` | `Array` | No | — | >= 1 items | | `filters.$or[].*.lt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.lte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.neq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.$or[].*.not_in` | `Array` | No | — | >= 1 items | | `filters.*` | `object` | No | Operators applied to one field. Several operators in one object combine as AND. | — | | `filters.*.eq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.exists` | `boolean` | No | — | — | | `filters.*.gt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.gte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.in` | `Array` | No | — | >= 1 items | | `filters.*.lt` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.lte` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.neq` | `string \| number \| integer \| boolean \| null` | No | — | — | | `filters.*.not_in` | `Array` | No | — | >= 1 items | | `name` | `string` | Yes | Human-readable name, for display. It takes no part in the id and cannot be changed. | — | | `slug` | `string` | Yes | Id segment chosen at registration. | — | ##### Response example ```json { "ancestry_attribute": "example", "catalog_id": "example", "collection_type": "example", "created_at": "2026-04-15T12:00:00Z", "id": "collection:category/member:product", "kind": "membership", "member_type": "example", "membership_attribute": "example", "updated_at": "2026-04-15T12:00:00Z" } ``` ### 400 Malformed request #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 401 Missing or invalid credentials #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 403 Authenticated but not permitted #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 404 Resource not found #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 422 Request validation failed #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ``` ### 503 The catalog's types could not be read, so the filter could not be checked against them. Retryable: `Retry-After` carries the number of seconds to wait. #### application/json Schema: `object` ##### Attributes | Attribute | Type | Required | Description | Constraints | |---|---|---|---|---| | `exception_details` | `object` | No | Machine-readable error context; empty object when there is nothing to add. | — | | `exception_details.error_code` | `string` | No | Stable machine code naming why a well-formed request was refused: snake_case, prefixed by its domain — e.g. `filter_unknown_field` or `cursor_mismatch`. Safe to branch on; `reason` is not. Absent when the status code says all there is to say. | — | | `exception_details.validation_errors` | `Array` | No | Every field-level problem found in the request, when validation is what failed. Absent when the rejection is not about a specific value. | — | | `exception_details.validation_errors[].loc` | `Array` | Yes | Path to the offending value from the request root, e.g. `["body", "filters", 0, "operator"]`. | — | | `exception_details.validation_errors[].msg` | `string` | Yes | What is wrong with the value at `loc`. | — | | `exception_details.validation_errors[].type` | `string` | Yes | Stable machine code for the problem, e.g. `missing` or `string_too_short`. | — | | `exception_details.*` | `any` | No | Additional property. | — | | `reason` | `string` | Yes | Human-readable, actionable error message. | — | | `request_id` | `anyOf(string, null)` | No | Request correlation ID (matches the X-Request-Id response header). | — | ##### Response example ```json { "exception_details": { "error_code": "example", "validation_errors": [ { "loc": [ "example" ], "msg": "example", "type": "example" } ] }, "reason": "example", "request_id": "example" } ```