Opti.ODP
1.0.2-beta
dotnet add package Opti.ODP --version 1.0.2-beta
NuGet\Install-Package Opti.ODP -Version 1.0.2-beta
<PackageReference Include="Opti.ODP" Version="1.0.2-beta" />
<PackageVersion Include="Opti.ODP" Version="1.0.2-beta" />
<PackageReference Include="Opti.ODP" />
paket add Opti.ODP --version 1.0.2-beta
#r "nuget: Opti.ODP, 1.0.2-beta"
#:package Opti.ODP@1.0.2-beta
#addin nuget:?package=Opti.ODP&version=1.0.2-beta&prerelease
#tool nuget:?package=Opti.ODP&version=1.0.2-beta&prerelease
Opti.ODP
Optimizely Data Platform (ODP) integration for Optimizely CMS 12. Provides visitor group criteria based on real-time ODP audiences with scheduled audience caching, encrypted cookie-based tracking, and a diagnostic API.
Features
- Visitor Group Criterion — Select ODP audiences from a dropdown in the CMS admin UI
- Scheduled Audience Sync — Automatically caches all ODP audiences every hour via a scheduled job
- Real-time Audience Matching — Fetches user audience membership from ODP on every page load via middleware
- Encrypted Cookie Tracking — Stores matched audiences in an encrypted
usr_segcookie (configurable) - Resilient Caching — Cache persists for 24 hours and is preserved if the sync job fails
- Load-balanced Support — Uses
ISynchronizedObjectInstanceCachefor multi-server environments - Fallback — Criterion reads from cookie first; if missing, fetches from ODP API and sets the cookie
Installation
1. Install NuGet Package
dotnet add package Opti.ODP
Or via Package Manager Console: Install-Package Opti.ODP
2. Configure in Startup.cs
public void ConfigureServices(IServiceCollection services) { // Register ODP services (before AddCms) services.AddOptimizelyOdp(Configuration); services.AddCms(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseAuthentication(); app.UseAuthorization(); // ODP middleware — fetches user audiences on every page load app.UseMiddleware<Optimizely.ODP.Middleware.OdpSegmentMiddleware>();
app.UseRouting(); app.UseEndpoints(endpoints ⇒ { endpoints.MapControllers(); endpoints.MapContent(); }); }
3. Add Configuration (appsettings.json)
{ "Optimizely": { "OdpSettings": { "PrivateApiKey": "your-private-api-key-here", "ApiHost": "https://api.zaius.com", "SegmentCookieName": "usr_seg", "SegmentCookieExpirationMinutes": 10, "SegmentCacheExpirationMinutes": 1440, "EncryptCookie": true } } }
Where to find your API key:
- Log in to your ODP account
- Navigate to Settings → APIs & Credentials
- Copy your Private API Key
4. Run the Audience Sync Job
- Go to Admin → Scheduled Jobs
- Find ODP Audience Sync
- Click Start Manually to populate the cache for the first time
- The job runs automatically every hour (configurable in Admin UI)
5. Restart Your Application
After installation and configuration, restart your application.
How It Works
Page Load │ ├─ Middleware (runs first on every page load) │ ├─ Reads vuid cookie │ ├─ Parses GUID (removes hyphens and timestamp) │ ├─ Calls ODP GraphQL API with cached audience subset │ └─ Writes/refreshes encrypted usr_seg cookie │ ├─ Visitor Group Criterion (runs during page render) │ ├─ Reads usr_seg cookie (fast, no API call) │ ├─ Decrypts and checks audience membership │ └─ If cookie missing → falls back to ODP API call │ └─ Response sent with fresh usr_seg cookie
Usage
Creating Visitor Groups
- In Optimizely CMS, go to Admin → Visitor Groups
- Create a new visitor group or edit an existing one
- Click Add Criterion
- Select Optimizely ODP → ODP Audience
- Choose an ODP audience from the dropdown
- Save the visitor group
Programmatic Usage
public class MyController : Controller { private readonly IOdpService _odpService; public MyController(IOdpService odpService) { _odpService = odpService; }
public async Task<IActionResult> Index() { var userId = Request.Cookies["vuid"] ?? User.Identity?.Name;
// Get all matched audiences for a user (calls ODP API)
var audiences = await _odpService.GetCustomerSegmentsAsync(userId);
// Check if user is in a specific audience
var isVip = await _odpService.IsInSegmentAsync(userId, "VIP Customers");
// Read audiences from cookie only (no API call)
var cookieAudiences = _odpService.GetAudiencesFromCookie(HttpContext);
// Get customer fields
var fields = await _odpService.GetCustomerFieldsAsync(userId, new[] { "email", "loyalty_tier" });
// Post an event to ODP
await _odpService.PostEventAsync("page_view", "viewed", new Dictionary<string, object>
{
["page_name"] = "Home",
["category"] = "Landing"
});
return View();
} }
Configuration Options
| Property | Default | Description |
|---|---|---|
PrivateApiKey |
(required) | Your ODP Private API Key |
ApiHost |
https://api.zaius.com |
ODP API endpoint |
SegmentCookieName |
usr_seg |
Cookie name for storing user audiences |
SegmentCookieExpirationMinutes |
10 |
Cookie expiration in minutes |
SegmentCacheExpirationMinutes |
1440 |
Cache expiration in minutes (default: 24 hours) |
EncryptCookie |
true |
Encrypt cookie using ASP.NET Data Protection |
Cookie Encryption
When EncryptCookie is true (default), the usr_seg cookie value is encrypted using ASP.NET Data Protection (AES-256).
EncryptCookie |
Cookie value | Readable? |
|---|---|---|
true |
CfDJ8N...base64... |
No — encrypted |
false |
["VIP Customers","Cart Abandoners"] |
Yes — plain JSON |
The package handles both formats seamlessly. You can switch between encrypted and plain at any time without losing existing cookies.
| Protection | How |
|---|---|
| Encryption | ASP.NET Data Protection API (AES-256-CBC) |
| Tamper-proof | HMAC signature — modification invalidates the cookie |
| HttpOnly | Not accessible via JavaScript |
| Secure | Only sent over HTTPS |
| SameSite=Lax | CSRF protection |
Scheduled Job
| Setting | Value |
|---|---|
| Name | ODP Audience Sync |
| Default interval | Every 60 minutes |
| Initial delay | 1 minute after startup |
| Cache TTL | 24 hours |
| Failure behaviour | Preserves existing cache |
| Configurable | Yes, via Admin → Scheduled Jobs |
The job output displays all synced audience names as a comma-separated list.
API Reference
IOdpService
| Method | Description |
|---|---|
GetCustomerSegmentsAsync(string userId) |
Fetch audiences from ODP API and refresh cookie |
GetAudiencesFromCookie(HttpContext httpContext) |
Read audiences from cookie only (no API call) |
GetAllSegmentsAsync() |
Fetch all audiences from ODP API |
GetAvailableSegmentsAsync() |
Get audience names (from cache or API) |
GetCachedSegmentKeysAsync() |
Get audience names from cache only |
RefreshSegmentCacheAsync() |
Refresh the audience cache from ODP API |
GetCustomerFieldsAsync(string userId, IEnumerable<string> fields) |
Retrieve customer data fields |
PostEventAsync(string type, string action, IDictionary<string, object> data) |
Send events to ODP |
IsInSegmentAsync(string userId, string segmentName) |
Check if user is in a specific audience |
Troubleshooting
Dropdown shows no audiences
- Run the ODP Audience Sync job manually from Admin → Scheduled Jobs
- Verify API credentials in
appsettings.json - Ensure audiences exist in your ODP account
- Check application logs for
ODP Audience Syncmessages
Cookie not being set
- Ensure the middleware is registered:
app.UseMiddleware<OdpSegmentMiddleware>() - Verify the ODP JavaScript snippet is on your site (sets the
vuidcookie) - Check that HTTPS is enabled (cookie has
Secureflag) - Check logs for
ODP Middleware: Parsed userIdmessages - Use the diagnostic endpoint:
GET /api/odp/my-audiences
User not matching any audiences
- Verify the
vuidcookie exists in the browser - Use the diagnostic endpoint:
GET /api/odp/check-user/{vuid} - Check application logs for
ODP GraphQL Response
Cache is empty after restart
The cache is in-memory and will be empty after an application restart. The scheduled job runs 1 minute after startup to repopulate it. You can also run it manually from Admin → Scheduled Jobs.
vuid Cookie Format
The ODP JavaScript snippet sets a vuid cookie in this format:
vuid=%7Cauth0|604dc23ae94c1f001c70a8f7|f79e9eca-6920-4cfb-9b5f-622480656f6c
The package automatically:
- URL-decodes (
%7C→|) - Splits on
|and takes the GUID - Removes hyphens →
56a87f1b25b44324aff3b5c9fa12ad1d
This is the format ODP expects for the vuid identifier.
Performance
At 200K+ requests/day:
| Operation | Cost | When |
|---|---|---|
| Cookie read + decrypt | ~0.02ms | Every request |
| ODP API call | 50-200ms | Once per page load (middleware) |
| Criterion evaluation | ~0.02ms | Reads cookie only |
The criterion reads from the cookie during page render. API calls only happen in the middleware before the page renders, or as a fallback if the cookie is missing.
License
MIT License
Support
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- EPiServer.CMS.Core (>= 12.23.1)
- EPiServer.CMS.UI.Core (>= 12.33.5)
- Microsoft.Extensions.Http (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|
v1.0.2-beta:
- Visitor group criterion with ODP audience dropdown
- Scheduled job (ODP Audience Sync) to cache all audiences hourly
- Real-time audience matching via middleware on every page load
- Encrypted cookie (usr_seg) using ASP.NET Data Protection (configurable)
- Cookie-first evaluation with ODP API fallback
- Diagnostic API endpoints for troubleshooting (admin-only)
- vuid cookie parsing (URL-decode, strip hyphens, remove timestamp)
- GraphQL subset query for per-user audience membership
- Resilient caching: preserves cache on job failure
- Load-balanced support via ISynchronizedObjectInstanceCache