Optimizely Product Recommendations – Implementation Nuances

Optimizely Product Recommendations
Implementation Nuances

A comprehensive guide to the four integration methods — JavaScript API, Server-to-Server API, Commerce Connect Native, and Configured Commerce Native — covering feed setup, tracking, rendering, and widget configuration.

📅 September 2026 👤 Dylan Walker · Optimizely

Overview & Comparison Matrix

A quick-reference view across all four implementation methods and key capability areas. Use this to determine the right approach for a given customer scenario.

Capability JS API (Standalone) S2S API (Standalone) Commerce Connect (Native) Configured Commerce (Native)
Relative Difficulty Medium Hardest Medium–Hard Easiest
Feed — Auto-generated? Manual/custom Manual/custom Via CMS export job Via CC RSS export job
Feed Type (Portal) Generic / Custom Download Generic / Custom Download Generic Upload (push) Generic Download (pull)
Feed Schedule Controlled In Product Rec Portal Product Rec Portal CMS Scheduled Jobs Product Rec Portal
Page Tracking — Auto? Manual JS tags Manual server POST Via [CommerceTracking] attribute Via peerius.js (base theme)
Click Tracking Manual (smartRecsClick) Manual (payload info.smartRecs.click) Manual (custom component) OOB via Product Carousel
Rendering Component Custom JS callback Custom (JSON response) Custom Razor views OOB Product Carousel widget
Authentication Domain whitelisting + script clientToken in payload Site tokens in appsettings.json Peerius URL + Site Name in Admin
Cookie Management Automatic (browser) Manual (server-managed) Automatic (CC package) Automatic (peerius.js)
Session Viewer — Cookie Location previous_user cookie (easy) No visible cookie; use URL method CUID in app (may have encoding) previous_user cookie (easy)
Mobile App Support Client-side only Required for mobile apps Partial
Widget Configuration Product Rec Portal Product Rec Portal Product Rec Portal Product Rec Portal + Spire CMS
💡 Key Principle Regardless of implementation method, product feed + page tracking + rendering are the three non-negotiable foundations. Widget configuration in the Product Recommendations portal is universal across all four methods. The primary differences lie in how these are set up and who controls each component.
⚠️ Do Not Mix JS and S2S on the Same Application JavaScript and Server-to-Server integrations cannot be blended within the same application (e.g., a website). They can coexist across different applications — for example, a website using JS and a mobile app using S2S within the same Product Recommendations instance.

Standalone · Client-Side JavaScript API

The most common standalone integration. All tracking and rendering happen client-side via the peerius.js script. Relies on browser cookies. Best for websites where server-side control is not required.

Feed Setup

The feed must be created and maintained by the customer or SA team. Optimizely does not auto-generate a feed for standalone integrations.

  • Feed formats: RSS 2.0 XML (preferred) or CSV — must be UTF-8 compliant.
  • Feed type in portal: Generic Download (standard) or Custom Download (if SA has modified the feed).
  • Feed URL: Set in the portal at Admin › Site Management › Feed Configuration › Configure.
  • Schedule: Configured directly in the Product Recommendations Portal (schedule tab). The customer must ensure their feed file is updated by the time Optimizely fetches it.
  • All products to be recommended must appear in the feed. Removing a product marks it inactive.
  • Include all attributes used in widget algorithm rules — attribute names must match exactly.
📌 Custom Download vs Generic Download Generic = feed is consumed as-is. Custom = an SA has applied a script to transform the feed before ingestion. If you see "Custom Download", Luke (or another SA) has likely written a modification script.

Tracking Setup (Page & Click)

Script Installation

Include peerius.js on every page, just above the </body> tag:

<script type="text/JavaScript"   src="//{sitename}.uat.productrecs.optimizely.com/tracker/peerius.page"   charset="UTF-8"> </script>

Configure tracking in the Admin Console integration tab:

SettingValue
Peerius Tracking URL (UAT)//{sitename}.uat.productrecs.optimizely.com/tracker/peerius.page
Peerius Tracking URL (Prod SE)//{sitename}.peerius.episerver.net/tracker/peerius.page
Peerius Tracking URL (Prod US)//{sitename}.uswe01.productrecs.episerver.net/tracker/peerius.page
Peerius Site NameYour instance name from the Rec Portal (e.g. datademosite2)
Peerius API Versionv1_4
⚠️ Do NOT include https:// or www in the tracking URL.

Page Tracking — PeeriusCallbacks

Declare the PeeriusCallbacks object on every page with apiVersion: "v1_4":

var PeeriusCallbacks = {     apiVersion: "v1_4",     // page-type tracking payloads go here };

Supported page types: home, product, category, basket, checkout, order, search, wishlist, brand, attribute. Contact Optimizely for custom page types.

Click Tracking

Click tracking must be manually implemented. Use the 64-bit recommendation ID (rec.id) returned in the smartRecs callback:

FunctionWhen to Use
Peerius.smartRecsClick(id)Click navigates to a new page
Peerius.smartRecsSendClick(id)User stays on the same page
⚠️ Without click tracking, the portal reports £0 Optimizely-attributed revenue. There is a 30-day attribution window — purchases within 30 days of a rec click are attributed.

Session Viewer — Cookie Location

Open DevTools › Application/Storage › Cookies. Look for the previous_user cookie. The CUID is between the colon (:) and the pipe (|). Format is clean and easy to read for JS implementations.

Rendering Setup

Recommendations are delivered via the PeeriusCallbacks.smartRecs JavaScript callback. You define this function; Optimizely calls it when recommendations are ready.

var PeeriusCallbacks = {     apiVersion: "v1_4",     smartRecs: function(jsonData) {         for (var i = 0; i < jsonData.length; i++) {             var widgetData = jsonData[i];             var position = widgetData.position; // Always use position, NOT widget name             var recs = widgetData.recs;             var container = document.getElementById(position);             if (!container || !recs) continue;             container.innerHTML = '';             for (var j = 0; j < recs.length; j++) {                 var rec = recs[j];                 var el = document.createElement('div');                 el.innerHTML =                     '<a href="' + rec.url + '?recommendationId=' + rec.id + '" ' +                     'onclick="Peerius.smartRecsClick(' + rec.id + ')">' +                     '<img src="' + rec.img + '" /><span>' + rec.title + '</span>' +                     '</a>';                 container.appendChild(el);             }         }     } };
💡 Always use position, never widget The widget name can change during A/B tests; position is stable and guaranteed unique. Binding your renderer to widget will break A/B test scenarios.

Widget Configuration

Widget setup is done entirely in the Product Recommendations Portal — identical process across all standalone implementations:

  1. Go to Configuration › Product Recommendations › Widgets › Create Widget
  2. Set Name, Page type, Position (stable ID used in rendering), and Number of Recommendations (1–50)
  3. Go to Widgets per page and assign widgets to relevant pages
  4. Add an algorithm stack (up to 11 algorithms; always include a fallback, e.g. popular products)
  5. For merchandising overrides, create a Campaign under Recommendations › New Campaign
⚠️ Do NOT re-activate widgets post-deployment. Re-activation resets widget configuration back to the default 6 widgets (home, category, search, product, basket). If widgets disappear post-deploy, contact Optimizely support — do not re-activate.

Standalone · Server-Side Server-to-Server (S2S) API

The most technically demanding implementation. All tracking is performed via server-side POST requests. Cookie management is entirely the client's responsibility. Required for mobile apps. Preferred when server-side control or cookie-bypass is needed.

✅ When to choose S2S over JS API
  • Customer has a mobile app requiring recommendations (JS has no client in this context)
  • Preference for explicit server-side cookie control (avoids browser cookie blocking)
  • Need to avoid third-party client-side script dependencies
  • Can be used alongside JS — one application can be JS, another S2S (e.g., website + mobile app)

Feed Setup

Feed setup is identical to the JS API — the customer supplies the feed file independently:

  • Feed type in portal: Generic Download or Custom Download
  • Schedule: Controlled in the Product Recommendations Portal (same as JS API)
  • RSS 2.0 XML preferred; must be UTF-8 compliant; include all attributes used in algorithm rules
  • Import historical orders before launch to accelerate algorithm learning

Tracking Setup (Page & Click)

Tracking Endpoint

EnvironmentURL
UAThttps://uat.productrecs.optimizely.com/tracker/smart-api/recommendations
Productionhttps://{sitename}.productrecs.optimizely.com/tracker/smart-api/recommendations

Method: POST · Format: JSON · Must use HTTPS · Requires clientToken in payload (from portal Admin › Site Management › Development)

Cookie Management — Critical S2S Distinction

⚠️ S2S does not auto-generate cookies. Unlike JS API where peerius.js manages peerius_user and peerius_sess cookies automatically, in S2S you must generate, store, and pass valid CUID (cuid) and session (session) values in every tracking payload. Missing or malformed values will prevent session tracking.
Cookie / FieldPurposeS2S Responsibility
peerius_user / cuidVisitor CUID — indefinite lifetimeClient-managed; passed in payload
peerius_sess / sessionSession ID — 4hrs from last activityClient-managed; passed in payload
peerius_ridTracks clicked recommendationsClient-managed

Cookie Format (v1.4)

In v1.4, the separator changed from / to |:

// v1.3 (deprecated) "session": "155780550/hKugo5RQsqecUpmlWE6EpFWqmalVrJU"  // v1.4 (current) "session": "123456789012|a1b2c3d4e5A6B7C8D9E0f1g2h3i4j5F1G2H3I4J5k1l" "cuid":    "09876543210|Z0Y9X8W7V6z5y4x3w2v1U0T9S8R7Q6u5t4s3r2q1P0O"

Page Tracking Payload Example

{   "type": "product",   "ip": "10.42.37.139",   "session": "123456789012|a1b2c3d4e5...",   "cuid": "09876543210|Z0Y9X8W7V6...",   "site": "retailer",   "clientToken": "1234abcd5678",   "channel": "web",   "lang": "en-gb",   "currentURI": "www.retailer.com/product/RC456.html",   "previousURI": "www.retailer.com/product/RC123.html",   "userAgent": "Mozilla/5.0 ...",    // Mandatory from v1.3+   "product": { "refCode": "RC456" } }

Click Tracking

Pass the recommendation ID from the previous response in the next tracking payload:

{   "info": {     "smartRecs": {       "click": 5637282    // 64-bit rec ID from previous smartRecs response     }   } }

Session Viewer — Finding Your Cookie

⚠️ Hardest integration to debug in Session Viewer. The CUID cookie often does not appear in browser DevTools because it is server-managed. Recommended approach:
  1. Navigate to a very obscure/low-traffic product URL on the customer's site
  2. Paste that URL into the Session Viewer search
  3. If the page was tracked, your session should appear
  4. Copy the CUID from the session — paste it back into the Session Viewer for future debugging
Note: Some customers drop a new cookie on every unknown user visit — in that case you may see two CUIDs. This is expected.

Rendering Setup

Recommendations are returned in the same JSON response as the tracking request — no separate callback needed. You receive a smartRecs array and are responsible for rendering it server-side or passing it to the front-end template.

ParameterDescription
recContent: "full"Returns full product details (price, image, title, URL) — default
recContent: "refCodeOnly"Returns only product ref codes + rec IDs (lighter payload)
smartProducts: ["smartRecs"]Use when multiple Optimizely products run on the same page
showAttributes: "*"Returns all product attributes in the response
📌 No user info = no personalisation If no CUID/session is sent, the response returns non-personalized recommendations (e.g. best sellers). Personalized recommendations require a valid, known CUID with prior browsing history.

Widget Configuration

Identical to the JS API — all widget configuration happens in the Product Recommendations Portal. The same widget/campaign/position concepts apply. The position value in the response determines which widget slot to render into.

  • Use position (not widget name) to identify render targets
  • Algorithm stacks, campaigns, and exclusions are configured identically in the portal
  • The clientToken for S2S is found at Admin › Site Management › Development in the portal

Native Integration · NuGet Package Commerce Connect (Native)

Installed via a NuGet package (EPiServer.Personalization.Commerce). Piggybacks on the Commerce Connect CMS infrastructure for feed and tracking. More out-of-box than standalone, but rendering is almost always custom-built due to the bespoke nature of Commerce Connect implementations.

Feed Setup

The feed is generated automatically by a CMS scheduled job — no manual feed file creation required.

How the Feed Works

The Export Product Feed job (available after installing the NuGet package) does two things:

  1. Serialises the Commerce Connect catalog to XML, compresses it, and stores it as a blob
  2. Notifies the Product Recommendations REST API that a new catalog is available (passing a callback address + auth token)

Optimizely then calls back to download the blob. This makes it a push/notify mechanism — not a URL-based pull.

⚠️ Feed type is NOT "Generic Download" Generic Download is the Configured Commerce approach. For Commerce Connect, the portal is set to Generic Upload — the feed is pushed from the CMS, not pulled from a URL.

Product Export Requirements

A product must meet all of the following to appear in the feed export:

RequirementDetail
In stockMust have available inventory
Valid pricesNon-expired, valid price entries
PublishedMust be published in CMS
Has variantsMust have at least one variant

Feed Schedule — Controlled in CMS

📌 Key Nuance — Schedule NOT in the Portal Unlike all other implementations, the feed schedule for Commerce Connect is controlled via CMS Admin › Scheduled Jobs — not in the Product Recommendations portal. This is the only integration where this applies.
JobPurposeRecommended Frequency
Export Product FeedFull catalog export (includes deletions, asset/inventory changes)Every 24 hours
Export Product Feed IncrementallyAdded/updated products only — does NOT replace the full jobAs needed

Key appsettings.json Configuration

{   "EPiServer": {     "Personalization": {       "PersonalizationOptions": {         "BaseApiUrl": "https://your-env.productrecs.episerver.net",         "Site": "your_site_name",         "ClientToken": "your_client_token",         "AdminToken": "your_admin_token",         "Channel": "web",         "TrackingMode": "ClientSide",         "UsePseudonymousUserId": true,         "FeedCatalogName": "YourCatalogName"       }     }   } }
💡 Site Tokens The ClientToken and AdminToken values (site tokens) are provided by Optimizely during onboarding via email. These must be placed in appsettings.json — unlike standalone implementations which use domain whitelisting or inline payload tokens.

Tracking Setup (Page & Click)

Page Tracking via CommerceTracking Attribute

Tracking is applied by decorating controller actions with [CommerceTracking(TrackingType.X)]. The tracking fires in OnActionExecuting, so recommendations are available immediately when the action method is entered:

[CommerceTracking(TrackingType.Product)] public ViewResult ProductPage(ProductPage currentPage) {     var recommendations = this.GetRecommendationGroups();     // Each Recommendation has:     //   .Area               → widget name (e.g. "Alternatives", "CrossSell")     //   .ContentReferences  → IEnumerable<ContentReference> of recommended products     var model = new ProductPageViewModel(currentPage) { Recommendations = recommendations };     return View(model); }

Supported TrackingTypes

Home · Search · Category · Product · Basket · Checkout · Order · Wishlist · Brand · Attribute

Click Tracking Nuance

Click tracking for Commerce Connect is not automatic (unlike Configured Commerce). It must be implemented in the custom rendering component. The 64-bit recommendation ID from the ContentReferences response is used — same approach as the standalone integrations.

Session Viewer — Cookie Location

For Commerce Connect, the CUID will be present in the application (DevTools). However, if the customer is using the server-side variant of the integration, the cookie format may differ — HTML encoding characters appear instead of the actual separator characters:

  • Standard format: cuid: 12345|abcdef... — look between : and |
  • Server-side encoded format: look between D and % (the HTML-encoded colon/pipe)
  • Client-side variant: standard cookie format, easy to read

Rendering Setup

⚠️ No ready-made UI widget — custom rendering is almost always required. Unlike Configured Commerce (which is a truly configured platform with consistent UI), Commerce Connect implementations are unique and bespoke. The NuGet package provides the data (GetRecommendationGroups()), but the rendering component must be built from scratch to match the site's UI/UX.

The out-of-box package does include a basic rendering option, but it is rarely used in practice. Build a custom Razor component:

@foreach (var group in Model.Recommendations) {     <div class="rec-widget" data-area="@group.Area">         @foreach (var productRef in group.ContentReferences)         {             var product = ContentLoader.Get<ProductContent>(productRef);             <!-- Render product card here -->         }     </div> }

Widget Configuration

Widget configuration follows the same portal-based approach as standalone integrations:

  • Configuration › Product Recommendations › Widgets — create and configure widgets
  • Widgets per page — assign to relevant pages
  • The Area property returned by GetRecommendationGroups() corresponds to widget names in the portal
  • Algorithm stacks and campaigns are configured identically to standalone methods

For multi-site setups, configure Scopes in appsettings.json — each scope maps to a different site/catalog and has its own ClientToken, Site, and BaseApiUrl.

Native Integration · Out-of-Box Configured Commerce (Native)

The simplest implementation. Configured Commerce handles feed, tracking, and rendering automatically via built-in integrations and the Spire CMS Product Carousel widget. A customer can often complete this implementation end-to-end following the official documentation with minimal technical overhead.

✅ Why it's the easiest
  • Feed is auto-generated via a scheduled export job
  • Page tracking & click tracking come out-of-box via peerius.js
  • Out-of-box Product Carousel widget handles rendering — just switch the carousel type to "Recommended Products"
  • No custom code required for a standard implementation
  • SCs can often lead the implementation on-call with the customer

Feed Setup

Step 1 — Enable in Admin Console

  1. Go to Admin Console › Administration › System › Settings
  2. Click the Integration tab › Integration Connectors
  3. Toggle Enable Product Recommendations to Yes

This auto-creates two scheduled jobs: Export Products RSS Feed and Export Historical Orders.

Step 2 — Schedule and Run the Feed Job

  1. Go to Administration › Jobs › Job Definitions
  2. Edit the Export Products RSS Feed job
  3. Set a schedule and fill in the Website Name
  4. After the job runs, verify the feed at: Library › Media › UserFiles › _system › ProductRecommendationFeed › Products_RSS_Feed-{WebsiteName}.xml

Step 3 — Import Feed into Product Recommendations Portal

  1. Go to Admin › Site Management › Feed configuration › Configure
  2. Set Feed type = Generic download
  3. Set Feed URL = {your_site_url}/api/v1/productsrssfeed
  4. Select your Default location (language code)
  5. Run the feed via the History tab
  6. Set a daily schedule — run 15–30 minutes after the CC export job
📌 Feed type for Configured Commerce = Generic Download The feed URL exposes an API endpoint from Configured Commerce that Optimizely pulls on schedule. Unlike Commerce Connect (push/notify), this is a direct URL pull.

Tracking Setup (Page & Click)

Automatic via peerius.js

peerius.js is automatically included in the base Configured Commerce themes. For custom themes/blueprints, add it manually just above </body>.

Configure the three settings in Admin Console › Integration tab:

SettingValue
Peerius Tracking URLEnvironment-specific URL (UAT / Prod SE / Prod US) — no https:// or www
Peerius Site NameInstance name from Product Recommendations portal (e.g. datademosite2)
Peerius API Versionv1_4

In the Product Recommendations Portal, add your domain (Admin › Tracking › Domains) and ensure the Script tab is set to Generic format.

Click Tracking

Click tracking comes out-of-box via the Product Carousel widget — no manual implementation required for standard setups. The widget handles the smartRecsClick call automatically.

Session Viewer — Cookie Location

Configured Commerce uses peerius.js under the hood — cookie format is identical to the JS API. Look for the previous_user cookie in DevTools. CUID is between the : and |. Clean, easy to read.

Rendering Setup

Out-of-Box Product Carousel Widget (Spire CMS)

  1. In Spire CMS, navigate to the target page
  2. Click Add Widget › select Product Carousel
  3. Set Carousel Type = Recommended Products
  4. (Optional) Set Widget Position = 2 for a second widget on the same page
  5. Toggle display options (price, rating, etc.) as needed
  6. Click Save › Publish
Carousel TypePlacement Restrictions
Recommended ProductsAny page — primary type for Product Recommendations
Top SellersCannot be added to Product Detail page
Customers Also PurchasedCart or Product Detail page only
💡 All UI looks the same Because Configured Commerce is a configured platform (not a custom one), the Product Carousel widget always looks the same across customers. This is why no custom rendering is needed here, unlike Commerce Connect.

Widget Configuration

Widget configuration uses a combination of the Product Recommendations Portal and the Spire CMS widget settings:

  • In the portal: create widgets, assign to pages, set algorithm stacks and campaigns (same as all other methods)
  • In Spire CMS: the Product Carousel widget's Widget Position field maps to the position configured in the portal
  • Multiple widgets on one page: use position 1 and position 2 in the carousel widget settings
  • All display toggles (show price, show rating, etc.) are controlled directly in the Spire CMS widget panel — no code required

Universal Concepts & Cross-Implementation Notes

These concepts and nuances apply regardless of implementation method. Understanding them is essential for troubleshooting and strategic consulting across all four integration types.

Feed Management — Configuration Tab

Feed TypeUsed ByMeaning
Generic DownloadJS API, S2S API, Configured CommerceOptimizely pulls the feed from a URL as-is, no modification
Generic UploadCommerce ConnectFeed is pushed from the CMS export job into Optimizely
Custom DownloadAny implementationSA has written a script to transform/modify the feed before ingestion
Custom UploadAny implementationUpload with SA-written transformation script applied
📌 Generic = no modification. Custom = SA has modified it. If you see "Custom" in the feed type, Luke or another SA has written an additional transformation script. Download = Optimizely pulls. Upload = customer/CMS pushes.

Session Viewer — Comparison by Implementation

ImplementationCookie NameFormatDifficulty
JavaScript APIprevious_userCUID between : and | — cleanEasy
Configured Commerceprevious_userSame as JS API — cleanEasy
Commerce Connect (client-side)CUID in appStandard format with actual colon/pipeEasy–Medium
Commerce Connect (server-side)CUID in appHTML-encoded: look between D and %Medium
Server-to-ServerNone visible in browserUse obscure URL method to find sessionHard

Widget Configuration — Universal

Widget building, campaign creation, and strategy management are identical across all four implementation methods. Once tracking and feed are in place, the portal experience is the same:

  • Widgets are created in Configuration › Product Recommendations › Widgets
  • Algorithm stacks support up to 11 algorithms — always include a fallback
  • Campaigns allow attribute-based rules and exclusions on top of widget strategies
  • A/B testing of widget strategies is available in all implementations
  • Use position (not widget name) for stable rendering references

Critical Implementation Rules

  • refCode consistency is mandatory across product, basket, checkout, and order pages — mismatches break behavior attribution and revenue reporting
  • lang field in tracking must match a locale in the latest feed export — mismatch causes empty recommendations
  • Domain whitelisting required in the portal for all standalone implementations (Admin › Tracking › Domains)
  • 30-day attribution window — clicks on recommendations attribute purchases for up to 30 days
  • Historical orders import before launch accelerates algorithm learning for all implementations
  • Service URL tab in the portal provides the base tracking script starter for standalone implementations — the site-specific suffix (e.g. USWE) must be appended to form the complete script URL
  • Do not re-activate widgets post-deployment — this resets widget configuration to defaults
✅ Strategic Consulting Note When working with customers on widget strategy, campaigns, A/B testing, and recommendation logic — there are essentially no differences between the four implementation methods. The nuances are primarily in the technical setup phase. Once live, the portal experience and optimisation workflow is identical.