Refine the product listing a shopper is on, and suggest how to narrow it next. This is the listing page experience: the shopper types a refinement such as only white or under $200, or taps a suggested filter value, and the agent turns that into filters. The response carries a short message about the results, one set of products, and usually a few choices that each apply one value of one facet, for rendering as chips. Render the listing with your own search or browse call as usual, and show the choices when they arrive rather than waiting for them.
page_context describes the listing: page_type is plp, with the search_term of a search results page, the group_id browse_filter of a category page, or both for a search inside a category. The agent narrows that listing but never leaves it: every result set is searched with that term or browses that category, and a search inside a category stays inside it. In page_context.filters, a filter whose match_type is absent or any, and whose name no other such filter has, is editable: the agent may change or remove it. Every other filter is fixed: it applies to every catalog call of the request and is never changed. shopper_input is the refinement the shopper typed. Leave it out when the page opens, after a choice is applied, and after the shopper changes filters on the page.
The item_results event reports the refined listing as listing: listing.page_context is the listing the results belong to, in the shape of a request page_context, and added and removed are the filters that changed. The service keeps no filter state between requests, so send listing.page_context as the next request's page_context, with the same thread_id. The choices are the choices of the follow_up_refinement event, each a label and the single filter it applies. To apply one, add its filter to listing.page_context.filters and send that with no shopper_input. A choice never names the category or a facet the listing already filters, and its value is always one of the facet options the results carry. On a facet that the integration lists as a path facet (path_facets), whose values are paths such as shoes/sneakers, a choice is dropped when another kept choice lies under its value: shoes is dropped next to shoes/sneakers. No choices are sent when fewer than two qualify, or when the results hold fewer than two products. That count covers the whole result set, not only the products in the item_results event, so a low num_results_per_event does not remove the choices. Choices always arrive after the results they refine. On an index that personalizes chips, the order of the choices, and on some facets the choices themselves, follow the shopper's history, read by i or ui. Do not share choices between shoppers.
A refinement that matches nothing can leave the listing as it was, with added and removed empty and a message that says what is missing. Filters that match no products return empty results and no choices: keep showing the previous listing, and send its page_context on the next request.
A stream opens with start and closes with end. In between it carries message events, at most one item_results, usually one follow_up_refinement and, on a domain with a knowledge base, article_reference events. server_error reports a failure after the stream has opened. A second follow_up_refinement is not ruled out, and the JSON response keeps the first. qs.filters is rejected, because it would override the listing's filters: send a fixed scope as qs.pre_filter_expression instead. There is no query classification, so a request is never answered with 422. Results stream back as server-sent events (text/event-stream) so the choices can be rendered as soon as they are produced. To receive the finished response as a single JSON document instead, send Accept: application/json.