Analytics and tracking
The widget reports two kinds of events to the backend:
- Automatic events (product clicks, detected conversions) emitted by the widget in response to user interaction.
- Manual events (custom business events) sent from your host via
POST /api/widget/analytics/eventor the batch endpoint.
All traffic flows through the X-Widget-Token header and shares the rate-limit policy of WidgetRateLimitFilter.
Automatic events
| Event | Endpoint | When |
|---|---|---|
| Product click | POST /api/widget/track/click | The user clicks on a card or CTA of a result. |
| Assisted conversion | POST /api/widget/track/conversion | The widget detects a conversion via conversionSelector (when tracking.conversions = true). |
Both endpoints are defined in WidgetTrackingController.java. The payload includes searchLogId and productId (plus optional orderId, orderValue, currency, timeToConversionMs, userSessionId on conversion) to close the attribution loop.
Manual events
For events the widget does not detect (banner impression, category click, etc.) use the generic endpoint:
/api/widget/analytics/eventcurl -X POST https://api.neuroon.ai/api/widget/analytics/event \
-H "X-Widget-Token: $WIDGET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventType": "category_click",
"searchLogId": "log_abc",
"productId": "p_1",
"metadata": { "category": "running" }
}'
Controller:
WidgetAnalyticsController.java.
Batch
To reduce round-trips from high-volume clients:
/api/widget/analytics/events/batchcurl -X POST https://api.neuroon.ai/api/widget/analytics/events/batch \
-H "X-Widget-Token: $WIDGET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": [
{ "eventType": "result_impression", "searchLogId": "log_abc", "productId": "p_1", "position": 1 },
{ "eventType": "result_impression", "searchLogId": "log_abc", "productId": "p_2", "position": 2 }
]
}'
The batch accepts up to 100 events per request (@Size(min = 1, max = 100) on BatchRequest).
Recommendation: batch 5–20 events or flush every 2 s, whichever happens first. navigator.sendBeacon is ideal for flush on unload.
Event schema
| Field | Type | Required | Notes |
|---|---|---|---|
eventType | string (max 30) | yes | Event identifier. Must match a value of WidgetAnalyticsEventType. |
searchLogId | string (max 255) | recommended | Ties the event to the search session. |
conversationId | string (max 36) | no | Conversation UUID. |
productId | string (max 255) | no | Related product id, when relevant. |
userSessionId | string (max 255) | no | Opaque user session id. |
responseType | string (max 20) | no | Agent response type, when relevant. |
enricherType | string (max 30) | no | Enricher that produced the event. |
position | integer | no | Position in the list (impression/click). |
metadata | object | no | Free-form pairs key → string | number | boolean (up to 20 entries). |
Exact validation:
EventRequestinWidgetAnalyticsController.java.
Rate limits
WidgetRateLimitFilter applies a different limit per endpoint family (all per minute, per shop):
| Endpoint family | Limit/min |
|---|---|
/api/widget/search/* | 200 |
/api/widget/suggestions | 300 |
/api/widget/compare | 10 |
/api/widget/analytics/* (event and events/batch) | 500 |
/api/widget/track/* (click, conversion) and the rest | 100 |
If you exceed the limit you receive 429 Too Many Requests with Retry-After, X-RateLimit-Limit and X-RateLimit-Remaining. See Authentication → Rate Limits.
Further reading
- Reference → Data models —
Eventschema. - API → Plugin tracking — server-to-server.
- Quickstart step 5 — end-to-end flow.