Experiences targeting via Javascript custom vars

It might be the case where you want to target users with signals managed by a DMP, CDP, CRM, A/B testing or other third party systems.

The Experience Manager allows targeting audiences based on dynamic properties injected via Javascript or via server-to-server data enrichment points.

On Experience Manager an audience can be created using a “Custom Variable” on ①. Once added the name and value conditions have to be defined on ②.

Last but not least, any custom variable defined as dynamic targeting, can be used as an expansion variable to generate the experience url ③.

Marfeel enables three ways to define custom vars:

  1. Synchronous definition on the Marfeel SDK configuration object
  2. Asynchronous definition using promises on the Marfeel SDK
  3. Defining a User, Session or Page Compass var

Synchronous custom vars definition

The recommended way to define custom vars for targeting is through the Marfeel SDK configuration object at initialization time. Key-value pairs of custom vars can be declared as follows:

{
experiences: {
   targeting:  {
      myCustomVar1: "myValue1",
      myCustomVar2: "myValue2",
   }
}

myCustomVar1 and myCustomVar2 will be available as custom vars in Experience Manager.

The complete SDK initialization script would look like:

<script type="text/javascript">
	function e(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],c=document.createElement("script");c.src=e,t?c.type="module":(c.async=!0,c.type="text/javascript",c.setAttribute("nomodule",""));var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(c,n)}function t(t,c,n){var a,o,r;null!==(a=t.marfeel)&&void 0!==a||(t.marfeel={}),null!==(o=(r=t.marfeel).cmd)&&void 0!==o||(r.cmd=[]),t.marfeel.config=n,t.marfeel.config.accountId=c;var i="https://sdk.mrf.io/statics";e("".concat(i,"/marfeel-sdk.js?id=").concat(c),!0),e("".concat(i,"/marfeel-sdk.es5.js?id=").concat(c),!1)}!function(e,c){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};t(e,c,n)}(window,
/* Your Marfeel AccountId here */,
/*config*/
{
experiences: {
   targeting:  {
      myCustomVar1: "myValue1",
      myCustomVar2: "myValue2",
   }
}
} );
</script>

A common use case for this mechanism is when the value of the targeting can be read synchronously from a cookie or from a JS that has been executed before.

Asynchronous custom vars definition

There are scenarios where the value of the customVar is not immediately available at the time of initializing the Marfeel SDK. Scenarios where a request to a third party system needs to be made are common use cases.

In this case you should use async promises. It’s important to highlight that until the promise is resolved the request to the targeting system won’t happen. If you are designing a flow that should execute in the critical path of User session we strongly recommend not using this mechanism.

ES2015 solution based on promises

Use this solution if you want to leverage modern browser promise APIs.

<script type="text/javascript">
   window.marfeel = window.marfeel || {};
   window.marfeel.cmd = window.marfeel. cmd || []:

   // Request to a third party to retrieve custom variables
   const getTargeting = fetch('https://targetings-provider.com?userId=000') ;
   const initializationPromise = new Promise(resolve => {
      window.marfeel.cmd.push(['experiences',  (experiences) => {
         getTargeting
            .then(response => response.json())
            .then(targeting =>
               experiences.addTargeting(targeting.key, targeting.value) ;
               resolve();
            });
         }])
   
    // Marfeel SDK initialization
	function e(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],c=document.createElement("script");c.src=e,t?c.type="module":(c.async=!0,c.type="text/javascript",c.setAttribute("nomodule",""));var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(c,n)}function t(t,c,n){var a,o,r;null!==(a=t.marfeel)&&void 0!==a||(t.marfeel={}),null!==(o=(r=t.marfeel).cmd)&&void 0!==o||(r.cmd=[]),t.marfeel.config=n,t.marfeel.config.accountId=c;var i="https://sdk.mrf.io/statics";e("".concat(i,"/marfeel-sdk.js?id=").concat(c),!0),e("".concat(i,"/marfeel-sdk.es5.js?id=").concat(c),!1)}!function(e,c){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};t(e,c,n)}(window,
    /* Your Marfeel AccountId here */,
    /*config*/
    {
     experiences: {
       waitFor: {
         promise: initializationPromise
       }
     }
    });
</script>

ES5 solution based on callbacks

This solution works in older browsers. The example is inspirational on how to leverage the donemethod of the Marfeel SDK.

<script type="text/javascript">
  window.marfeel = window.marfeel || {};
  window.marfeel.cmd = window.marfeel.cmd || [];

  function getJSON(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'json';
    xhr.onload = function () {
      var status = xhr.status;
      if (status === 200) {
        callback(null, xhr.response);
      } else {
        callback(status, xhr.response);
      }
    };
    xhr.send();
  };

  getJSON('https://targetings-provider.com?userId=000', function (targeting) {
    window.marfeel.cmd.push(['experiences', function (experiences) {
      experiences.addTargeting(targeting.key, targeting.value);
      
      // Call this once you are ready
      experiences.done()
    }]);
  });

    // Marfeel SDK initialization
	function e(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],c=document.createElement("script");c.src=e,t?c.type="module":(c.async=!0,c.type="text/javascript",c.setAttribute("nomodule",""));var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(c,n)}function t(t,c,n){var a,o,r;null!==(a=t.marfeel)&&void 0!==a||(t.marfeel={}),null!==(o=(r=t.marfeel).cmd)&&void 0!==o||(r.cmd=[]),t.marfeel.config=n,t.marfeel.config.accountId=c;var i="https://sdk.mrf.io/statics";e("".concat(i,"/marfeel-sdk.js?id=").concat(c),!0),e("".concat(i,"/marfeel-sdk.es5.js?id=").concat(c),!1)}!function(e,c){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};t(e,c,n)}(window,
    /* Your Marfeel AccountId here */,
    /*config*/
    {
     experiences: {
       waitFor: {
         done: true
       }
     }
    });
</script>

User, Session and Page Compass vars as targeting custom vars

Last but not least, any pageVar, sessionVar or userVar informed to the Marfeel Compass tracker will also be piped as a custom var available for targeting in real time.

<script>
	window.marfeel = window.marfeel || {};
	window.marfeel.cmd = window.marfeel.cmd || [];
	window.marfeel.cmd.push(['compass', function(compass) {
		compass.setPageVar('samplePageVar', 'value');	
		compass.setSessionVar('sampleSessionVar', 'value');	
		compass.setUserVar('sampleUserVar', 'value');	
	}]);

	// Marfeel SDK initialization
	function e(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],c=document.createElement("script");c.src=e,t?c.type="module":(c.async=!0,c.type="text/javascript",c.setAttribute("nomodule",""));var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(c,n)}function t(t,c,n){var a,o,r;null!==(a=t.marfeel)&&void 0!==a||(t.marfeel={}),null!==(o=(r=t.marfeel).cmd)&&void 0!==o||(r.cmd=[]),t.marfeel.config=n,t.marfeel.config.accountId=c;var i="https://sdk.mrf.io/statics";e("".concat(i,"/marfeel-sdk.js?id=").concat(c),!0),e("".concat(i,"/marfeel-sdk.es5.js?id=").concat(c),!1)}!function(e,c){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};t(e,c,n)}(window,
/* Your Marfeel AccountId here */,
/*config*/ {} );
</script>

This mechanism is useful in the context of dynamic paywalls, where the paywall decides dynamically which articles should be closed. You might exclude experiences from dynamically closed pages (informed via a pageVar).

It’s also the recommended approach if you are running sticky session or sticky user A/B tests. This way you’ll be able to analytically compare complete sessions using Marfeel’s raw data module.

Further considerations

There are some important considerations to bear in mind specially when setting up A/B tests with third party tools:

Reading a cookie synchronously

A/B test cookies may not be set at the time that the Marfeel SDK configuration is generated. On the first page of the user, the custom var will probably be empty and the user won’t be targeted, resulting in less flowcards shown to the experiment group.

Reading a cookie asynchronously

In case the A/B testing system is slow it can delay by several seconds the targeting mechanism which will result in less Opens and Clicks on the Marfeel Experiences.

The recommendation would be to generate the testing groups inline and send them to the testing system and the Marfeel targeting SDK.

2 Likes