Cross-sell
When the host has cart.enabled = true, the widget can ask the backend for complementary recommendations on top of the current cart: products commonly bought together with the present items, discarding those already in the cart or already seen. It is rendered with CrossSellSection.
Endpoint
/api/widget/cart/cross-sellcurl -X POST https://api.neuroon.ai/api/widget/cart/cross-sell \
-H "X-Widget-Token: $WIDGET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"productIds": ["p_iphone_15"],
"language": "en"
}'
Defined in
WidgetProductController.java(body:CartCrossSellRequestDTO { productIds: string[], language?: string }). Returns{ message, products[] }. When no relevant recommendations exist, the endpoint replies 204 No Content with no body.
Difference vs cartAction.ADD_SUGGESTION
There are two channels through which the widget recommends adding a product:
| Channel | Source | UX |
|---|---|---|
POST /api/widget/cart/cross-sell | The widget calls the endpoint when the cart drawer mounts or after item changes | A section with several products in the drawer / cart page. |
cartAction.ADD_SUGGESTION in SearchResponse | The conversational agent proactively decides (during a search) that suggesting a specific product makes sense | Premium card with confirmationPrompt, a single suggestion. |
Ranking
The backend uses signals such as:
- Historical co-purchase (closed carts with both products).
- Category compatibility (declarative rule per shop).
- Margin and stock (penalizes out-of-stock).
- Deduplication (does not return products already in the cart).
The recommendation engine lives in the Python engine, not in this widget repo. If you need to inspect the specific logic, open an issue with support.
When it is called
- When opening the cart drawer if no data is cached.
- While the drawer is open and cart items change (600 ms debounce in
useCartCrossSell.ts:90). The listener does not auto-fire on everyneuroon:cart-update— only while the drawer is visible. - Manual:
widget.openCart().
Tracking
A click on a cross-sell card is reported as a normal event (POST /api/widget/track/click) with metadata.source = 'cross_sell', which lets you measure real lift vs baseline.
Further reading
- Cart integration
- Configuration — enable
cart. - Analytics and tracking