Zen Journey - a technical view on the Adobe Marketing Cloud - part 1

30 January 2015
Michal Papciak
Frink_Cognifide_2016_HeaderImages_0117

To be able to deliver the journey described here, we need to put together multiple puzzles. What we need tool-wise is:

- Adobe Analytics

- Adobe Target

- Adobe Dynamic Tag Manager

- Adobe Campaign

- Adobe Experience Manager

We are going to focus on more high level concepts rather than very specific implementations, but you should be able to easily push those ideas into your own scenarios.

As a first part, we are going to describe the AEM side in details, its communication with Campaign, as well as some Analytics. So, as you can remember from the
journey description, John was our visitor, potentially interested in buying a new car. We are going to use some diagrams to better understand which technology is used on what step and what needs to be done to put all of this together.

pic1

When John wanders on our page, which is served by AEM, he is subjected to some A/B tests created for anonymous visitors as we want to offer the most relevant content. During John's journey we are able to figure out his vehicle of interest and send it to Analytics.

This is a very important step, in which we start building John’s profile. At this point, John is still anonymous to us, which is why we are going to use Marketing Cloud Visitor ID service for identification purposes. You will learn more details about it in the second part of this blogpost.

What we want to do now, is to put the Visitor ID along with his car of interest to Adobe Campaign. This can be achieved either by: extracting data from Analytics Data Warehouse and transferring it to a specific place via sFTP. From here the script / job can pick it up and load it to Campaign’s Visitor and Customer tables. Refer to http://microsite.omniture.com/t2/help/en_US/whitepapers/ftp/ftp_sftp_dw.html to see how you can extract data from Analytics Data Warehouse using the API of both tools.

We decided to use the APIs as this is more convenient and allows for more flexibility. Let’s see how we can get the data out of Analytics (note that in order to do that, you need to have web services credentials as well as a web service key available in Analytics). Then you need to send a request similar to the following:

URL: https://api.omniture.com/admin/1.4/rest/?method=Report.Queue

{  
 "reportDescription":{  
  "reportSuiteID":"yourRSID",
  "date":null,
  "dateFrom":"2014-12-01",
  "dateTo":"2015-01-29",
  "dateGranularity":"Day",
  "metrics":[  
   {  
    "id":"theEventYouWant"
   }
  ],
  "sortBy":null,
  "elements":[  
   {  
    "id":"evar1",
    "classification":null,
    "top":null,
    "startingWith":null,
    "search":null,
    "selected":null,
    "parentID":null,
    "pattern":null,
    "checkpoints":null,
    "everythingElse":null
   },
   {  
    "id":"evar2",
    "classification":null,
    "top":null,
    "startingWith":null,
    "search":null,
    "selected":null,
    "parentID":null,
    "pattern":null,
    "checkpoints":null,
    "everythingElse":null
   }
  ],
  "segments":null,
  "locale":null,
  "currentData":null,
  "anomalyDetection":null,
  "expedite":null,
  "elementDataEncoding":null,
  "sortMethod":null,
  "source":null
 }
}

Now, our eVar1 is actually Visitor Id, whereas eVar2 is used to store the vehicle of interest. Interestingly, in the response, you will get something similar to:

{
  "reportID":"(int)"
}

Tricky, huh? What you need to do with is to send another request to https://api.omniture.com/admin/1.4/rest/?method=Report.Get, in here you need to pass the reportId given in the previous response. Finally, you will get a response with all the data you requested.

If you wish to know more on how to create proper requests, please refer https://marketing.adobe.com/developer/documentation/analytics-reporting-1-4/get-started.

You can also use Adobe API explorer for training: https://marketing.adobe.com/developer/api-explorer.

You will probably get a lot of data, but for your reference here is a small sample:

{  
 "report":{  
  "type":"trended",
  "elements":[  
   {  
    "id":"evar1",
    "name":"Marketing Cloud Visitor ID"
   },
   {  
    "id":"evar2",
    "name":"Car of interest"
   }
  ],
  "reportSuite":{  
   "id":"cog-scpoc",
   "name":"Summit Campaign POC"
  },
  "period":"Mon.  1 Dec. 2014 - Mon. 22 Dec. 2014",
  "metrics":[  
   {  
    "id":"event3",
    "name":"My Event",
    "type":"number",
    "decimals":0,
    "latency":632,
    "current":false
   }
  ],
  "data":[  
   {  
    "name":"Mon. 1 Dec. 2014 - Mon. 22 Dec. 2014",
    "year":2014,
    "month":12,
    "day":1,
    "breakdown":[  
     {  
      "name":"25830268269091131336124353906128820461",
      "url":"",
      "counts":[  
       "4"
      ],
      "breakdown":[  
       {  
        "name":"::unspecified::",
        "url":"",
        "counts":[  
         "4"
        ]
       }
      ],
      "breakdownTotal":[  
       "4"
      ]
     },
    ],
    "breakdownTotal":[  
     "4"
    ]
   }
  ],
  "totals":[  
   "4"
  ],
  "version":"1.4.14.10"
 },
 "waitSeconds":"0.272",
 "runSeconds":"1.839"
}

You are now ready to load the data to Campaign. This can be also achieved by using a restful service. Campaign is very flexible when it comes to services - not only it gives you out-of-the-box APIs, but also allows you to create your own. The crucial part is that you need to populate one of the two most important Campaign tables: the visitor table. For now, let’s save your visitor Id and vehicle of interest in the Visitors table. Our basic profile for (still anonymous to us) John is now ready.

pic2

John returns to our website and we know that he is a returning visitor. While John’s exploring the page in search of his dream car, we use Adobe Target to serve John one of two versions of newsletter subscription overlays, what allows us to optimize our overlays at the same time. John decides to sign up. We track this fact with Analytics at the same time gathering his email address. What is left to do now is to update our knowledge base about John. We can again use the Analytics Data Warehouse API to update the Campaign Visitor table. We can create an API call similar to the previous one, in which we would ask for Visitor IDs, for which the email breakdown is not empty. We should also get the vehicle of interest. Apart from updating Visitor table in Campaign, we should also move the data about John to Customer table. John has now willingly given us his email, which means he is no longer anonymous to us.

Now Campaign is finally ready to send some emails to John. This is the crucial part. Let’s first have a look at the email John will receive:

2015-02-04_10h12_19

The hero image section would be a placeholder for a picture of choice for Air Conditioning service, the message would prompt John to use the service, whereas Promo1 would contain John’s vehicle of interest and prompt John to contact dealer for a free ride.

That kind of email can be build in Campaign. The important thing is that you need to include proper links underneath your Call to Actions / Images. Let’s have a look at the example link:

www.yourcarpage.com/acservice?receipientid=1&blockid=1&campaignid=1&contentid=1

Let’s say this is a link for your Air Conditioning service picture at the very top of the email. The parameters would mean:

receipientid = 1 - id of the visitor, in our case John

campaignid = 1 - our Air Conditioning service campaign

blockid = 1 - there are 3 blocks with images in our email - hero, promo1, promo2. Number one is for hero.

contentid = 1 - form Analytics we know that John is interested in a specific car. This ID represents the content for that car.

Note that at this stage all of those parameters are available for Campaign. We know John’s ID (being a key given by Campaign for specific Visitor ID, email, or the Visitor ID or email itself), his car of interest (contentid), our campaigns and emails we create ourselves. You can create the emails for John and other users that you have data for. What happens when your visitors get their emails?

pic4

When John gets to the Air Conditioning service page with a form, the URL will contain the parameters from the email. He should get the content which is personalised for him. Our page has 2 placeholders for this kind of content and a form:

pic5

We need two final elements to make it work:

1. Within Adobe Campaign you need to create an API that will accept the following set of parameters:

- receipientid

- campaignid

- blockid

- contentid

- placeholderid

Placeholder IDs is where our dynamic components lay on the page. On the mock above, those will be the 2 images under the main Air Conditioning service hero image. Those components now send a request to Campaign API with all above parameters - receipientid, campaignid, blockid, contentid are obtained from the URL, whereas placeholder id is different for every component on the page. This way the API should respond with a unique content for each dynamic component on this page, in this case with content designed specifically for John. In our scenario the API returned the proper image that needs to be rendered (as we only used image components for personalisation) but you could easily think of something else - perhaps the entire HTML, etc.

2. The second thing we need is a component which will dynamically change for our visitor. We used a standard image component but expanded it to some degree. In our component’s dialog we added an option to select the placeholder.

Also, have a look at the content stream. This, in theory, could store the types of content you would like your component to get from the Campaign API. Again, in our demo we only used images.

With dialog in place, we can now extend our component model:

package com.cognifide.poc.core.components.campaign;

import com.cognifide.slice.api.provider.ModelProvider;
import com.cognifide.slice.mapper.annotation.JcrProperty;
import com.cognifide.slice.mapper.annotation.SliceResource;
import com.cognifide.zg.webapp.commons.base.image.ImageModel;
import com.cognifide.zg.webapp.foundation.helpers.DimensionHelper;
import com.cognifide.zg.webapp.foundation.helpers.ValidationHelper;
import com.google.inject.Inject;

@SliceResource
public class CampaignModel extends ImageModel {

 @JcrProperty
 private String contentStream;

 @JcrProperty
 private String placeholderId;

 @Inject
 public CampaignModel(ModelProvider modelProvider, ValidationHelper validationHelper,
   DimensionHelper dimensionHelper) {
  super(modelProvider, validationHelper, dimensionHelper);
 }

 public String getContentStream() {
  return contentStream;
 }

 public String getPlaceholderId() {
  return placeholderId;
 }
}

As you can see we are using Slice here. If you are not familiar with the topic, please refer to our Github page - https://github.com/Cognifide/Slice.

Finally, we need a simple Javascript that will send request to our Campaign API. This could be similar to:

/**
 * campaign.js
 */
ZG.component.campaign = (function($) {"use strict";

 var api = {};

 api.init = function(elements) {
  elements.each(function() { //goes through each element matching the .campaign > .component-content selector
   var id = $(this).data("placeholderId"), 
    uri = "yourcampaignserviceaddress", 
    component = this, 
    campaignId = getParameterByName("campaignId"), 
    userId = getParameterByName("userId"), 
    blockId = getParameterByName("blockId");

   if (id && campaignId && userId && blockId && userId) {
    uri = uri + "?campaignId=" + campaignId + "&userId=" + userId + "&blockId=" + blockId + "&placeholderId=" + id;
    $.get(uri, function(data) {
     if (data) {
      component.innerHTML = data;
     }
    });
   }
  });
 };

 function getParameterByName(name) {
  name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
  var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
  return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
 }

 return api;

})(ZGjQuery, document);

ZG.register({
 name : "campaignPoc",
 api : ZG.component.campaign,
 selector : ".campaign > .component-content"
});

In here we simply go through each campaign-ready component (with ‘.campaign’ class) and change its content (‘.component-content’) dynamically by making the request to Campaign API. Note that we use all the parameters from the URL + the placeholderid that you can set on your component dialog.

After John sees the personalised content he decides to sign up for the service. We now gain the info about his current car and zip code. We also know his name now, so our emails can have a salutation. We can again update his profile in the Campaign database as we did earlier. With each and every campaign, we will get more info about John, his preference and behavior. And thanks to that our content will become more and more relevant. This is where the real power of combining Analytics, Target and Campaign arises - the more we know about John, the more likely we will build a relevant experience and make him convert.

pic6

You should just remember to use the tools properly. For optimising content, Adobe recommends to use Target for anonymous users and Campaign for users we can identify. Build and update your Campaign Customer database consequently.

Stay tuned for the next part of this blog, in which we will focus on technical implementation of Visitor ID service via Dynamic Tag Management, analytics tracking and using Target to serve John the newsletter overlays and personalized content.