This documentation explains how to integrate your webpages/portal with Pixel Customer Experience Script (CX Script)
The CX Script is a JS script that runs in the client's browser. The CX Script gathers timings and metrics for any page on which it is placed, and additionally serves to correlate the user between sites on which it is placed. It also allows HTML pages to pass custom data fields. The metrics collected by the script along with Custom Fields sent by the page are sent to Pixel as Client Side Events (CSE) or simply Events.
NOTE : To allow correlation of various ID's in use throughout UHG, the CX Script must be called from the pixel repo servers using the above URLs.| Environment | URL | Description |
|---|---|---|
| Stage | https://repo-stg.rakanto.com/rakanto/cx/cx.js | Point to this URL for your lower environment |
| Prod | https://repo.rakanto.com/rakanto/cx/cx.js | Point to this URL for your production deployment |
Make remote calls to the Pixel Repo URLs. Do not download the script or place in your source control.
Make CX Script the first thing in your HTML. This helps the CX Script to collect accurate metrics on the page.
<html>
<head>
<script type="text/javascript">
(function (cx, darids, demarcs) {
window['RakantoObject'] = 'Rakanto';
window['Rakanto'] = window['Rakanto'] || function () {
(window['Rakanto'].q = window['Rakanto'].q || []).push([Date.now()].concat(Array.prototype.slice.call(arguments)))
}, window['Rakanto'].l = Date.now(), window['Rakanto'].demarcs = demarcs;
a = document.createElement('script'),
a.src = cx,
a.id = 'rakanto',
a.async = true,
(darids)? a.setAttribute('data-px-darids', darids):null,
m = document.getElementsByTagName('script')[0],
m.parentNode.insertBefore(a, m);
})('https://repo-stg.rakanto.com/rakanto/cx/cx.js', 'UHG.Optum.Pixel.CXScriptExample');
</script>
</head>
<body>
<h1>Pixel Customer Experience Script - A simple example</h1>
</body>
</html>
Just copy the above code in an HTML file and open it in a browser. That's it.
UHG.Optum.Pixel.CXScriptExample in the example above is a Digital Analytics Reporting ID (DARID). Details about DARIDs can be found in later sections.
Data collected by the CX Script can be viewed and analyzed in the Pixel Self Service Kibana.
STAGE -- Click to View the CSE Self Service STAGE Kibana Desktop
PRODUCTION -- Click to View the CSE Self Service PRODUCTION Kibana Desktop
(Login with your MSID and Password)
Filter in Kibana on YOUR-DARID. Details about DARIDs can be found in later sections.
Note : Please reach out to Pixel team if you need help accessing/using the above KibanasUBRID is a unique ID embedded in CX Script source code, this allows it to act as a "Super Cookie".
UBRID is generated in the following format
<UBRID> ::= <Version>-<ServerID>-<PID>-<WorkerPID>-<WorkerTimeStamp>-<SeqNum>-<CurrentTimestamp>
<Version> ::= "v2.0"
<ServerID> ::= MD5 Hash of the name of the server generating UBRID
<PID> ::= Process Id of the Parent Process that generates the UBRID
<WorkerPID> ::= Process Id of the child Process that generates the UBRID. A parent process usually has 1 to 8 worker processes
<WorkerTimeStamp> ::= The timestamp at which the worker Sequence number started (Please refer to SeqNum below for more information). This timestamp is UNIX timestamp in milliseconds
<SeqNum> ::= A running number from 1 to 1,000,000,000,000. The Sequence number is specific to worker process.
The sequence number will reset to 1 when the process restarts or when the counter reaches 1,000,000,000,000.
The WorkerTimeStamp will be set to the current time when the sequence number resets.
In other words, WorkerTimeStamp is the time stamp when the sequence number was 1 for the worker process
<CurrentTimestamp> ::= Current UNIX timestamp in millisecondsExample : v2.0-7c1b733fa81543ed7af89b72687005d6-20613-20617-1636659751012-0000076890-1637253735012
Browser gets the CX Script from the Pixel Repo Server that runs on the Rakanto domain UBRID is also set in a cookie in the browser by Rakanto Server. Expiration for that cookie is two years
The expiration of the script itself is set at 30 minutes. So every 30 minutes, the browser will check whether the script has changed. This means whenever a new script version is released, the browser will not have an old version for more than 30 minutes.
When the browser checks for updates, if the script has changed, a new script will be downloaded by the browser. The repo server will check for the cookie and if it is present, it will get the UBRID from the cookie and set it in the Script. If the cookie is not present or if the UBRID is not present in the cookie, a new UBRID will be generated.
The Repo server will set the UBRID in the CX Script itself. So the script has it until browser tries to download the new version of the script.
The CX Script stores the UBRID in a global variable window.Rakanto.ClientSideData.ubrid
The CX Script creates an additional cookie in the website's domain, and places the UBRID and additional information in the new cookie. This cookie is used for our Server Side Processing of the events. This code is also known as Scriptlet
The Digital Analytics Reporting ID, is a tag which identifies a business unit within UHG. These IDs are used to identify and group your events in Pixel System.
Note : DARIDS need to be AllowListed! Work with Pixel Team to get the DARID(s) you should tag your pages withProposed DARID format consists of 2 main components, Business Information, Application Information
<DARID> ::= <Business Information>.<Application Information>
<Business Information> ::= <Entity>.<Sub-entity>.<Line of Business>
<Entity> ::= "UHG"
<Sub-entity> ::= "UHC" | "Optum"
<Line of Business> ::= "EnI" | "MnR" | "CnS" | "Global" | "Rx"
<Application Information> ::= <Application>[.<Function>][.<GroupLevel1>... .<GroupLevelN>]
<Application> ::= denotes application name and is a required element.
<Function> :== Additional elements in DARID that specifies the subset of Functionality
<GroupLevelx> :== Additional elements in DARID that can be used to group pages or user experience. Use periods to separate the application from the groups/functionsExample 1: login page in MnR Member Medicare solution implementation would tag the page with the following DARID UHG.UHC.MnR.MedicareSolutions.Member.LoginPage
Note: Pixel will be generating more DARIDs based on the DARID provided. For example, the above DARID UHG.UHC.MnR.MedicareSolutions.Member.LoginPage will be parsed into
UHG.UHC
UHG.UHC.MnR
UHG.UHC.MnR.MedicareSolutions
UHG.UHC.MnR.MedicareSolutions.Member
UHG.UHC.MnR.MedicareSolutions.Member.LoginPageExample 2: login page shared by Mnr Medicare Solutions Member page and Acquisition page may pass two DARIDs UHG.UHC.MnR.MedicareSolutions.Member.LoginPage and UHG.UHC.MnR.MedicareSolutions.Acquisition.LoginPage
Note : Pixel will be generating the following DARIDs from the DARIDs provided above
UHG.UHC
UHG.UHC.MnR
UHG.UHC.MnR.MedicareSolutions
UHG.UHC.MnR.MedicareSolutions.Member
UHG.UHC.MnR.MedicareSolutions.Member.LoginPage
UHG.UHC.MnR.MedicareSolutions.Acquisition
UHG.UHC.MnR.MedicareSolutions.Acquisition.LoginPageIn the stage environment, the CX Script will automatically send in a special DARID UHG.Optum.Pixel.SelfService along with the DARIDs set in the page. This DARID (UHG.Optum.Pixel.SelfService) is allow listed in Pixel Environment allowing events to flow through the Pixel Pipeline in the stage environment without needing any additional setup.
Clients will need to work with Pixel team to make sure their primary DARID is Allow listed in Pixel Production environment.
Darids may be declared in different places depending on their expected lifetime. When the CX Script reports something the DARID set used is chosen in the following precedence, highest to lowest.
As a direct input into an API call.
From attributes of the script tag The code to include the script on your page. This is the easiest way call the CX Script.
From a session object Store the DARIDs in sessionStorage. CX Script will use the DARIDs defined in Session Storage
<html>
<head>
<script type="text/javascript">
sessionStorage.setItem('darids','UHG.Optum.Pixel.CXScriptExample.DaridInSessionStorage');
(function (cx, darids, demarcs) {
window['RakantoObject'] = 'Rakanto';
window['Rakanto'] = window['Rakanto'] || function () {
(window['Rakanto'].q = window['Rakanto'].q || []).push([Date.now()].concat(Array.prototype.slice.call(arguments)))
}, window['Rakanto'].l = Date.now(), window['Rakanto'].demarcs = demarcs;
a = document.createElement('script'),
a.src = cx,
a.id = 'rakanto',
a.async = true,
(darids)? a.setAttribute('data-px-darids', darids):null,
m = document.getElementsByTagName('script')[0],
m.parentNode.insertBefore(a, m);
})('https://repo-stg.rakanto.com/rakanto/cx/cx.js');
</script>
</head>
<body>
<h1>Pixel Customer Experience Script</h1>
<h2>Darids from session object</h2>
</body>
</html>
From the page data layer.
Pass one or more DARIDs using the data layer (window.optumPageDataLayer). The window.optumPageDataLayer is used to set DARIDs that gets sent in the pixel call
<html>
<head>
<script type='text/javascript'>
window.optumPageDataLayer = {
darids: ['UHG.Optum.Pixel.CXScriptExample.DaridsFromPageDataLayer']
};
(function (cx, darids, demarcs) {
window['RakantoObject'] = 'Rakanto';
window['Rakanto'] = window['Rakanto'] || function () {
(window['Rakanto'].q = window['Rakanto'].q || []).push([Date.now()].concat(Array.prototype.slice.call(arguments)))
}, window['Rakanto'].l = Date.now(), window['Rakanto'].demarcs = demarcs;
a = document.createElement('script'),
a.src = cx,
a.id = 'rakanto',
a.async = true,
(darids)? a.setAttribute('data-px-darids', darids):null,
m = document.getElementsByTagName('script')[0],
m.parentNode.insertBefore(a, m);
})('https://repo-stg.rakanto.com/rakanto/cx/cx.js');
</script>
</head>
<body>
<h1>Pixel Customer Experience Script</h1>
<h2>Darids from Page Data Layer</h2>
</body>
</html>
In Stage and Dev environments there are test pages, which send requests to Kibana. You can model your calls after these pages if you would like.
https://repo-stg.rakanto.com/rakanto/js-test https://repo-dev.rakanto.com/rakanto/js-test
In addition to DARIDs partitioning your data for reporting, custom data may be sent along as well. The purpose of which may be for A/B testing, reporting on site specific metrics, or just to capture site specific information.
The custom data is an object in the form of
CUSTOM_DATA_OBJECT = {'namespace':'darid_from_darid_list_on_page','key1':'value1','key2':'value2', ... }
custom fields will show up in Event as:
CF_*namespace*_key1=value1 {'namespace':'darid_from_darid_list_on_page','key1':'value1'}
If the field exceeds that length, an error message is placed in JavaScript log, and the base64 encoded customData is set to cx_error_${event}` = 'send custom data size exceeded limit' this will show up in event.NOTE : Please do not send custom data messages from events which will rAPIdly fire, such as mouse movements.
We have tested against very large loads, however when we tested this exact scenario, one browser was generating over 1000 requests per second.Custom Data may be declared in different places depending on their expected lifetime. The key values in these locations are not "merged".
When the CX Script has Custom Data set in multiple locations, the CX script selects the location to represent custom data, in the following precedence, highest to lowest.
```javascript
CUSTOM_DATA_OBJECT = {"namespace":"darid_from_darid_list_on_page","key1":"value1","key2":"value2", ... }
```API call
Make a method call from your JavaScript.
window.Rakanto.event("sendCustomData",CUSTOM_DATA_OBJECT)
sessionStorage.getItem("pxCustomData");
Session storage only accepts STRINGS. A STRING must be made from your CUSTOM_DATA_OBJECT
To do this, JSON.stringify your CUSTOM_DATA_OBJECT first.
Assign that string to the sessionStorage.pxCustomData object, the custom data will be sent on each pixel request, overriding any custom data on optumPageDataLayer.
sessionStorage.setItem( "pxCustomData", JSON.stringify( CUSTOM_DATA_OBJECT ) )
Example
<html>
<head>
<script type="text/javascript">
sessionStorage.setItem('darids','UHG.Optum.Pixel.CXScriptExample.CustomData');
custom_data_object = {'namespace':'UHG.Optum.Pixel.CXScriptExample.CustomData','key1':'value1'}
sessionStorage.setItem('pxCustomData', JSON.stringify(custom_data_object) );
(function (cx, darids, demarcs) {
window['RakantoObject'] = 'Rakanto';
window['Rakanto'] = window['Rakanto'] || function () {
(window['Rakanto'].q = window['Rakanto'].q || []).push([Date.now()].concat(Array.prototype.slice.call(arguments)))
}, window['Rakanto'].l = Date.now(), window['Rakanto'].demarcs = demarcs;
a = document.createElement('script'),
a.src = cx,
a.id = 'rakanto',
a.async = true,
(darids)? a.setAttribute('data-px-darids', darids):null,
m = document.getElementsByTagName('script')[0],
m.parentNode.insertBefore(a, m);
})('https://repo-stg.rakanto.com/rakanto/cx/cx.js');
</script>
</head>
<body>
<h1>Pixel Customer Experience Script</h1>
<h2>Custom Data</h2>
</body>
</html>
window.optumPageDataLayer
The window.optumPageDataLayer can also have a DARID list, used for calls on the page. If custom data is specified, those key values are sent as well.
How to send custom data:
{"namespace":"YOURDARID, "key1":"value1", ... }
CF_YOURDARID_key1The custom data will be sent with each Pixel request.
<html>
<head>
<title>CX Script Page</title>
<script type="text/javascript">
window.optumPageDataLayer = {
darids: ["UHG.Optum.Pixel.CXScriptExample.CustomData"],
pxCustomData: {"namespace":"UHG.Optum.Pixel.CXScriptExample.CustomData","key1":"value1"}
};
(function (cx, darids, demarcs) {
window['RakantoObject'] = 'Rakanto';
window['Rakanto'] = window['Rakanto'] || function () {
(window['Rakanto'].q = window['Rakanto'].q || []).push([Date.now()].concat(Array.prototype.slice.call(arguments)))
}, window['Rakanto'].l = Date.now(), window['Rakanto'].demarcs = demarcs;
a = document.createElement('script'),
a.src = cx,
a.id = 'rakanto',
a.async = true,
(darids)? a.setAttribute('data-px-darids', darids):null,
m = document.getElementsByTagName('script')[0],
m.parentNode.insertBefore(a, m);
})('https://repo-stg.rakanto.com/rakanto/cx/cx.js');
</script>
</head>
<body>
<h1>Pixel Customer Experience Script</h1>
</p>(Darids from a Window Page Data Layer)</p>
</body>
</html>
Using in an Angular site.
see Single Page Apps
open Chrome inspector and view the network tab, click record
in a Chrome console window, type:
window.Rakanto.event("setUserIdentity",{ "authSystem": "SomeAuthSystem", "userId": "bob.dobbs", ... })
// or set more than one auth system at once..
window.Rakanto.event("setUserIdentity",{ "authSystem": "SomeAuthSystem", "userId": "bob.dobbs", ... },{ "authSystem": "AnotherAuthSystem", "userId": "bob.dobbs", ...})
// or send an array of authsystems..
window.Rakanto.event("setUserIdentity",[{ "authSystem": "SomeAuthSystem", "userId": "bob.dobbs", ... },{ "authSystem": "AnotherAuthSystem", "userId": "bob.dobbs", ...}])
open Chrome inspector and view the network tab, click record
in a Chrome console window, type:
window.Rakanto.event("sendCustomData",{ "namespace": ONE_OF_YOUR_DARIDS, "key1": "value1", "key2": "value2", ... })
OR
To verify custom data, on macOS:
In Chrome inspector, look at the get request issued. Copy the encryptedData value, from the querystring in headers
run the command in osx terminal pbpaste | base64 -D -
This will base64 decode the value, your original hash data will show up.
The history of browser pages, or SPA app pages viewed are often important fields for correlating a user journey through the app. We capture URL in multiple places, and at multiple times a user interacts with a page.
pageURL is window.location, at the time the page was loaded.
The startPageLoad event contains the userPageHistory, an array, of arrays.
The startViewLoad event contains the userViewHistory, an array of arrays
We DO NOT send corresponding top level current VIEW.location fields similar to pageURL with userViewHistory generating events.
We DO send pageURL with EVERY event. However the pageURL is a snapshot in time, when the page was loaded, If it is the same as the current viewHistoryURL, it's purely coincidental
The cx script exposes a few global methods you can use to send events. First you need to allow those global methods within your app.
declare global {
interface Window {
Rakanto: any;
}
}The following code is a sample app-routing.module.ts it sends a starViewEvent every time the router detects a navigation change. The most likely event you want to monitor.
export class YourRoutingModule {
constructor(private router: Router) {
router.events.subscribe((event: Event) => {
// console.log(event);
if (event instanceof NavigationEnd) {
(async() => {
// console.log("waiting for window.Rakanto.event() to be available.");
while(!(window.hasOwnProperty("Rakanto") && window.Rakanto.hasOwnProperty('event'))){
await new Promise(resolve => setTimeout(resolve, 20));
}
window.Rakanto.event('startViewLoad');
})();
}
});
}pxStartViewLoad will reset timers, and update the page history with the current view url. A startViewLoad event reflecting these will be sent.
This code will send a 'viewLoaded' event. This event shows how long the view took to load.
window.Rakanto.event("viewLoaded")This example supports sending a custom data event with every mouse click. The event contains the tagName of the element clicked, though this could be changed to also contain the class, div, or even custom HTML attributes, where you can choose your own tags.
import { Directive, HostListener } from '@angular/core';
declare global {
interface Window {
Rakanto: any;
}
}
@Directive({
selector: '[pixelCX]'
})
export class PixelCXDirective {
@HostListener('window:click', ['$event'])
onClick(event: any) {
this.log(`You clicked on ${event.target.tagName}`);
let CUSTOM_DATA_OBJECT = {
"namespace": "UHG.Optum.Pixel",
"action": `clicked ${event.target.tagName}`
};
window.Rakanto.event("sendCustomData", CUSTOM_DATA_OBJECT);
}
}Look in your browsers "developer tools : network inspector" for a "cx_collector" event. You will notice custom_data in the event is encrypted. If you need to verify the call, you will need to look at decrypted data in Kibana.
From Kibana Filter on the page darid, and look for your particular call. The decrypted content will show.
We sent the previous call with the namespace
"UHG.Optum.Pixel"
Custom Data field will show in Kibana as:
"CF_UHG.Optum.Pixel_action": "clicked ${event.target.tagName}",
The CX Script server includes a header
add_header Cache-Control 'private';This header makes it so the CX Script is not cached by edge caches, and is only cached by the browser. The CX Script is custom for each user. There are variables which are inserted into the script by the CX server, These variables are used for a correlation ID, API endpoints and other things.
When the cache expires a session is checked when the new file is downloaded, based on the session values, variables are inserted into the new script.
Requirements from the State of California and European Union nations require us to provide a method for opting out of data collection. Please provide your customers with a link to https://cse.rakanto.com/optout.htm to facilitate opting out of data analytics. Data is still collected for security purposes, however it will not be visible to the business.
Users may change their opt-out preference at any time. The opt-out feature is tied to the pixel-ubrid cookie, set by the cx script. If this cookie is reset, opt out preferences will be forgotten until a user log's in.
For sites using the CX script, If a user opts out on any UHG or Optum site, they are opted out of all sites.