Marketing Smart with Einstein Analytics – Part 5


By now I have posted four blogs on how you can use Einstein Analytics for Marketing Cloud reporting. In the blog series we have covered how to get Marketing Cloud prepped, how to set up the connector in Einstein Analytics and also how to generate a dataset on open and click tracking data using the dataflow. So at this point, it’s really up to you to be creative, and by the way, I would love to hear what you have done, so please do drop a comment if you want to share what you have done. Anyway, there is one thing left to show. One thing that will lift your dashboards and make them even more powerful; sent your data back into a journey in Marketing Cloud with a bulk action!

Yup, it is possible! But I cannot take credit for this one cause I have never been a Salesforce developer and since you need Apex and Visualforce for bulk actions then this one is out of my skill set. Luckily I know and work with some absolutely brilliant people one of them being Brenda Campbell a Marketing Cloud Solution Architect. So we can all thank Brenda for this insight.

Creating a marketing journey

The use case was to have a dashboard in Einstein Analytics where you can segment your contacts. Now once you have segmented who you want to target you can add these to a journey in Marketing Cloud. So the first thing we need to do is, of course, make sure we have a Marketing Cloud journey as well as a data extension that triggers the journey and hence where we need to push our contacts to.

So login to Marketing Cloud and navigate to Email Studio or Contact Builder to create your new data extension.

In Email Studio in the “Subscriber” tab choose “Data Extensions”.

Now click “Create” in the top right corner, choose “Standard Data Extension” and click “Ok”.

Now give your data extension a name, it doesn’t really matter what you call it. Also, make sure you check the sendable checkbox and click “Next”.

For the data retention policy, you can just click “next” without making any changes.

We need to add four fields to the data extension:

  • ContactID – type text – length 20 – primary key is true
  • EmailAddress – type email
  • Date_Added – type date – nullable is true – default current date
  • Sent_Email – type boolean – nullable is true – default true

Also, make sure the “Send Relationship” is “ContactID” before you click “Create”.

Note that you can add more fields but if you want them populated from Salesforce then you need to make sure you later modify the Apex.

Next, we need to create a journey that uses this data extension, before we get started on that make sure that you have an email ready to use for this journey. However, in this guide, I am not going to walk through how to create an email.

Now navigate to the Journey Builder by hovering over “Email” and choose “Journey Builder” – twice.

In the top right corner go ahead and click “Create New Journey” and “Create Journey from Scratch”.

First up is, of course, to give your journey a name. Next, take the “API Event” and drag it into the “Start with an Entry Source”.

Then drag over the email in the step next to the “API Event”.

Click on the orange wait period to change it from 1 day to 1 minute, so contacts exit shortly after being sent the email.

Now let’s click on the “API Event” in order to define the entry source, which will be our data extension we created. So choose the data extension and click “Next”.

In this scenario, we don’t need to apply a contact filter so just skip this step and click “Next”. And when you see the summary screen notice and copy the Event Definition Key and paste it in a text document, we will need it later. Once you have that just hit “Done”.

Now we need to define the email we will be using, so click on the “Email” in the canvas, the activity you dragged over earlier. Find and select the email that you want to use and click “Next”.

Click “Next” until you get to the summary slide. You can of course choose to change some of the settings, but I am sticking with the default setup. Finally, make sure you agree to the setup and then hit “Done”.

We now need to define if contacts can be added multiple times and what should happen if they do. So go to the settings by clicking the gear symbol.

I’ve chosen “Re-entry only after exiting” but it doesn’t really matter what you select here, you just need to make a decision on the contact entry.


The final step in the journey creation is to make sure you save and activate your journey.

One more note before we jump to Salesforce is that we need to go back into the installed package – you would have created it following the step in the first blog in this series.

First thing here is to make sure you as a minimum have “Read” and “Write” access to the data extensions. Next, you want to copy and paste the Client Id and Secret together with your event definition key. If you are unsure how to do this, make sure you follow the steps in the first blog.

Prepping Salesforce

The assumption here is that you have the Marketing Cloud Connect set up to make sure that the two platforms are connected. One thing the app comes with is a connected app, which we will also use for this solution. Don’t worry we don’t actually have to do anything about this, it’s just important that it’s there.

First up we need to set up two remote sites so we can access Marketing Cloud from Salesforce. So in Salesforce navigate to setup and search for “Remote Site” in the quick find. Click “New Remote Site”, give it a name and enter this URL:

Click “Save & New”.

Give the second remote site a name and use the following URL:

Then click “Save”.

Next up we need to create an Apex class, which we will call “AddToJourneyController”. Again you can use the quick find and search for “Apex classes” and click on “New”.

Now add the following code:

public class AddToJourneyController {
 public string query{get; set;}
/* To determine the records to perform the bulk action on, extract the SAQL query */
 public PageReference init() {
 query = ApexPages.currentPage().getParameters().get('query');
 return null;

/* Takes the contact records from the SAQL query, calls the AddToJourney method to add to SFMC data extension and inject into journey */
 public static Map<String, String> create(List <Map<String, String>> contactRecords) {
 Map<String, String> result = new Map<String, String>();
 String accessToken = getAccessToken();
 for (Map<String, String> contactRecord : contactRecords) {
 String email= contactRecord.get('Email');
 String contactId = contactRecord.get('Id');
 System.debug('Adding ' + email + ' and Id: ' + contactId + ' to SFMC using accessToken: ' + accessToken);
 AddContactToJourney(accessToken, contactId, email);
 return result;

/* Connecting to SFMC with client ID and Secret */
 public static String getAccessToken() {
 String clientId = 'XXXXXXXXXXX';
 String clientSecret = 'XXXXXXXXXX';
 Http http = new Http();
 HttpRequest request = new HttpRequest();
 request.setHeader('Content-Type', 'application/json;charset=UTF-8');
 request.setBody('{"clientId":"'+ clientId + '","clientSecret":"' + clientSecret + '"}');
 HttpResponse response = http.send(request);
 // Parse the JSON response
 if (response.getStatusCode() != 201) {
 System.debug('The status code returned was not expected: ' +
 response.getStatusCode() + ' ' + response.getStatus());
 } else {
 String accesstoken = null;
 JSONParser parser = JSON.createParser(response.getBody());
 while (parser.nextToken() != null) {
 if (parser.getCurrentToken() == JSONToken.FIELD_NAME) {
 String fieldName = parser.getText();
 if (fieldName == 'accessToken') {
 accesstoken = parser.getText();
 System.debug('accesstoken => ' + accesstoken);
 return accesstoken;
/* Determine which journey and DE to add the contacts to */
 public static HttpResponse AddContactToJourney(String accessToken, String contactId, String emailAddress) {
 System.debug('Inside AddContactToJourney and Adding ' + emailAddress + ' and Id: ' + contactId + ' to SFMC using accessToken: ' + accessToken);
 String bearerToken = 'Bearer ' + accessToken;
 String reqBody = '{"ContactKey": "' + contactId + '", "EventDefinitionKey": "APIEventXXXXXXXXXXXXXXXXXXXXX", "Data":{"ContactID": "' + contactId + '", "EmailAddress": "' + emailAddress +'"}}';
 System.debug('reqBody is: ' + reqBody);
 Http http = new Http();
 HttpRequest request = new HttpRequest();
 request.setHeader('Content-Type', 'application/json;charset=UTF-8');
 request.setHeader('Authorization', 'Bearer ' + accessToken);

 HttpResponse response = http.send(request);
 // Parse the JSON response
 if (response.getStatusCode() != 201) {
 System.debug('The status code returned was not expected: ' +
 response.getStatusCode() + ' ' + response.getStatus());
 } else {
 System.debug('The request executed ok ' + response);
 return response;

The code does several things, first it takes the contacts from SAQL query in Einstein Analytics, then it connects to Marketing Cloud and pastes the contacts from Einstein Analytics into the data extension that powers your journey.

As you hopefully can see I highlighted three things in this Apex class, which we will need to modify. You hopefully copied and pasted the Client Id, Secret, and API definition because we need them now.

Where it says “String clientId” and “String clientSecret” make sure you replace the X’s with the correct values from the installed package.

String clientId = 'XXXXXXXXXXX';
String clientSecret = 'XXXXXXXXXX';

Next, you need to modify the API definition key. So the next bit that is highlighted make sure to replace the “APIEventXXXXXXXXXXXXXXXXXXXXX” with the API definition key from the event entry source in your journey.


Finally hit “Save”.

A note to the Apex class is that you can, of course, modify this to not having all the keys hardcoded and instead use custom settings. This approach would also allow you to make the journey selection more flexible as right now people would ever only be added to this journey.

Now the Apex class is not enough, we need to make sure we can trigger it as well, so for this, we will use a visualforce page.

Go to the quick find again and search for “Visualforce”, choose “Visualforce Pages” and click “New”. Give your new page a name, I’ve called mine “Add To Journey”. The API name or simply name is what is important to remember because we need that later when we connect the final dots.

We, of course, need to add the code, so copy and paste the following code into the visualforce markup.

<apex:page controller="AddToJourneyController" action="{!init}" showheader="false" sidebar="false" standardStylesheets="false" title="Add To Journey" >
 <apex:stylesheet value=""/>

<apex:includeScript value=""/>
 th {
 width: 50%;
 h4 {
 font-size: 24px;
 table {
 font-size: 20px;
 width: 100%;
 <div class="container-fluid">
 <h4 id="message">Querying Contacts...</h4>
 <table name="results" id="results" data-role="table" class="table-bordered table-striped table-responsive">
 $(function() {
 headers: {"Authorization": 'Bearer {!$Api.Session_ID}'}
 setTimeout(executeQuery, 1000);
 function executeQuery() {
 var query = {};
 query.statements = "{!JSENCODE(query)}";
 var queryObj = {query: query.statements};
 type: 'POST',
 url: '/services/data/v39.0/wave/query',
 data: JSON.stringify(queryObj),
 contentType: 'application/json',
 success: function(data) {
 $('#message').html('Adding to journey...');
 var record = null;
 var row = null;
 $('#results tbody').empty();
 for (var i = 0; i < data.results.records.length; i++) {
 record = data.results.records[i];
 row = $('<tr>');
 row.append($('<td class="' + record['Email'] + '">').html('Complete...'));
 $('#results tbody').append(row);
 setTimeout(function() {addToJourney(data.results.records);}, 1000);

/* Calls the Apex controller method that add contact to the journey. */
 function addToJourney(contactRecords) {
 AddToJourneyController.create(contactRecords, function(result, event) {
 if (event.status) {
 for (var i = 0; i < contactRecords.length; i++) {
 $('td.' + contactRecords[i].Id).html(result[contactRecords[i].Id]);
 $('#message').html(contactRecords.length + ' Contact added to journey in Marketing Cloud');
 else {
 $('#message').html('Error: ' + event.message);

We don’t have to do any modifications here, so just go ahead and click “Save”.

Again if you want to modify the styling of the visualforce page, knock yourself out. You could allow the user to make selections or make it look like lightning. So if you are visualforce shark then be creative.

Connecting the dots

Yay! We are now ready to connect the dots and power the journey with contacts from Einstein Analytics using the Apex class and visualforce page we created.

Navigate to Einstein Analytics and make sure you have a dashboard ready to use – of course, powered by a dataset with the contact as the root object. Have whatever you want on this dashboard but as a minimum, we need a values table that contains the id and the email. Mine looks like this.

In edit mode find the widget of your value table. Notice that in the general section there is a checkbox for Show custom action, we need to select that one.

You will now need to give your action a label, meaning what will the users see when they look in the drop down. I’ve just called mine “Add to Journey” but you could, of course, be more specific about what journey you are adding the contacts to. We also need to add the API name of the visualforce page we just created, so the action know which page to load.

And that’s it! You can now preview your dashboard, make some selections and test out the bulk action in the value table by clicking on the drop-down menu in the table and select the action you just enabled.

Just note that we in the Marketing Cloud journey defined an email send, that does mean an email will be sent to the contacts that are in your table, so make sure that if you are just testing this out that you have your own email in it.

Connected clouds

Hopefully, you can see the power of bulk actions, we can not only get data from Marketing Cloud into Einstein Analytics, but we can we can segment our contacts in Einstein Analytics and add them to a marketing journey. So Brenda proved that this is possible, but you can, of course, take this solution even further. Maybe you want to be able to select the marketing journey your contacts to be added to. You can also make your journey more complex and add different channels within the journey. The opportunities are endless.

How useful was this post?

Click on a star to rate useful the post is!

Written by

4 thoughts on “Marketing Smart with Einstein Analytics – Part 5”

  • 1
    Jonny on April 6, 2019 Reply

    Thanks for this Rikke. Just to note on creating Apex Classes, did you need to use a Sandbox/Developer org in order to create this before you were able to use it in your Einstein Production instance?

    • 2
      Rikke on April 6, 2019 Reply

      I did it in a trailhead org. This was not a production use case. But when it comes to apex you always want to run it in a sandbox first to make sure nothing breaks.

  • 3
    Monika on December 4, 2019 Reply

    Hi Rikke,
    Thanks for the great post.

    I am stuck on getting the client Id and secret key. I have created a package and added the components as well in Marketing Cloud, got the client Id and the secrety key also. But I am always getting 403 error when I try with those keys and this end point “”, can you please help ?

    • 4
      Rikke on December 9, 2019 Reply

      Are you using an installed package with OAuth 2.0?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.