Visitor Analytics
Self-hosted visitor journey, cart abandonment, product recommendations, search analytics — one plugin, one database, no third party.
What it does
Built for production from day one.
Self-hosted analytics that reaches all the way from a visitor's first pageview to a recovered abandoned cart. Journey drawer with parsed UA + MaxMind geo + heuristic intent labels; cart abandonment detection with signed recovery links and Slack notifications; co-view product recommendations (also-viewed, personal, trending); site-search analytics (top queries, zero-result gaps, search-to-cart conversion); rage-click and dead-click hot-spot lists. Privacy-first defaults: DNT respected, IPs anonymised, optional consent gate. Ships a drop-in storefront JS helper at `/ees/hulo.js` — one script tag and every event type is wired.
Cart abandonment
Detects sessions with `cart_snapshot` events but no `checkout_completed` in the abandonment window (default 30 min). Auto-promotes to `converted` when a matching checkout later lands. Signed recovery-link tokens (time-bounded, non-reusable) — `/ees/abandoned-carts/:id/recovery-link` returns a URL you drop into a recovery email. Slack notification for high-value drops.
Product recommendations
Denormalised `ProductCoView` table rebuilt every 6 hours from `product_view` events, bounded per session to 20 events so bot sessions can't skew the table. Three endpoints: `also-viewed` for a product-page rail, `personal` for a homepage/cart rail based on a returning visitor's last 10 product views, `trending` for a most-viewed-in-window rail.
Site search analytics
Zero-schema-cost queries over `search` events. Top queries by volume with average results count, zero-result queries (direct catalogue-gap intel), search-to-cart conversion rate.
Journey drawer buffs
Rage-click + dead-click hot-spot lists per URL. Per-session heuristic `intent` label (`purchase` / `abandon` / `frustrate` / `consider` / `browse` / `bounce`) — one glance per session in the visitor drawer.
Drop-in storefront helper
Ships `/ees/hulo.js` — one script tag and every event helper (`cartSnapshot`, `productView`, `search`, `checkoutCompleted`) is on `window.hulo`. Handles batching, `sendBeacon`, auto rage-click + dead-click detection.
Configurable conversion goals
CRUD a goal with a URL glob (`/checkout/thank-you/*`) and a value. Live matcher tags every pageview that hits the pattern. Dashboard shows completions per goal.
Full visitor journey
Page views, time-on-page, exit pages, configurable funnel, UTM attribution, bot detection. Per-visitor profile drawer with parsed UA + MaxMind GeoLite2 geo. Survives login — guest and signed-in events share the same visitor id.
Privacy-first defaults
DNT respected, IPs anonymised to /24 (IPv4) / /48 (IPv6), optional `requireConsent` gate. All three opt-outable per install.
Live-now SSE widget
Real-time tile on the admin dashboard showing visitors active right now (by country).
CSV export
`/ees/visitors/export.csv?days=N` and `/ees/abandoned-carts/export.csv` for raw event / abandoned-cart data.
Admin dashboards
Angular admin pages for Abandoned Carts (KPIs, filters, recovery-link mint, CSV export) and Analytics Insights (trending, also-viewed lookup, search analytics, rage/dead-click hot spots).
Install
Three steps, five minutes.
Add the package
Or run the one-line installer that does steps 1–3 for you:
Prefer to do it by hand?
Register it
In your vendure-config.ts:
Run the migration
The plugin adds its own table(s). Generate + run the migration like any other:
That's it. The admin UI tab appears immediately. Without a licence key the plugin runs in a degraded evaluation mode — fine for trying things out. Buy a key →
HTTP endpoints
Every route exposed.
/ees/trackPublic: ingest a batch of events/ees/hulo.jsPublic: drop-in storefront helper JS (0.8.1)/ees/recover-cart?t=…Public: resolve a recovery-link token → cart items/ees/recommendations/also-viewed?productId=…Public: co-view recommendations for one product/ees/recommendations/personal?visitorId=…Public: personalised recs from visitor history/ees/recommendations/trending?hours=…Public: most-viewed products in the window/ees/abandoned-cartsAdmin: paginated list with filters/ees/abandoned-carts/summaryAdmin: totals + recovery rate + lost value/ees/abandoned-carts/:idAdmin: detail incl. parsed items/ees/abandoned-carts/:id/recovery-linkAdmin: mint signed recovery URL/ees/abandoned-carts/:id/statusAdmin: mark recovered / dismissed/ees/abandoned-carts/export.csvAdmin: CSV export/ees/search-analytics/topAdmin: top search queries/ees/search-analytics/no-resultsAdmin: zero-result queries/ees/search-analytics/conversionAdmin: search→cart conversion/ees/journey/rage-clicksAdmin: rage-click hot spots/ees/journey/dead-clicksAdmin: dead-click hot spots/ees/journey/session-summary?visitorId=…Admin: per-session intent labels/ees/visitors/summaryAdmin: top-line counters + daily series/ees/visitors/sourcesAdmin: top sources by visits / sessions/ees/visitors/top-pagesAdmin: most-visited URLs/ees/visitors/funnelAdmin: configurable funnel with drop-offs/ees/visitors/exit-pagesAdmin: top exit pages/ees/visitors/liveAdmin: SSE live-now stream/ees/visitors/journey/:visitorIdAdmin: per-visitor timeline/ees/visitors/export.csvAdmin: CSV export/ees/goalsAdmin: create a conversion goal/ees/goals/statsAdmin: per-goal completion statsFAQ
Common questions.
How do I get a licence key?
Buy here — Stripe Checkout, monthly or lifetime. You'll receive the JWT key by email; set it as HULO_LICENCE_KEY_VISITOR_ANALYTICS in your .env.
Does it work without a key?
Yes — the plugin boots in a degraded "evaluation" mode. You can install, configure, and inspect the admin UI before committing.
Where is data stored?
In your Vendure database. The plugin adds its own tables via a migration — your data never leaves your server.
Will it survive a Vendure upgrade?
Tested against Vendure >=3.5.0 <4.0.0 — 3.5, 3.6 and 3.7 are all covered by CI. A boot-time compatibility check emits a non-fatal warning if @vendure/core is outside that range, so upgrades to a future 3.x are safe to try. The 4.0 line will be tested and re-declared once its changelog lands.