Skip to main content

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

POST/api/widget/cart/cross-sell
curl -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:

ChannelSourceUX
POST /api/widget/cart/cross-sellThe widget calls the endpoint when the cart drawer mounts or after item changesA section with several products in the drawer / cart page.
cartAction.ADD_SUGGESTION in SearchResponseThe conversational agent proactively decides (during a search) that suggesting a specific product makes sensePremium 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 every neuroon: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