How to fix wrong Decision Split evaluation in SFMC

While creating some basic journeys in Salesforce Marketing Cloud I discovered strange behavior when using decision splits in journeys with multiple entries per contact. After thorough debugging, writing reports and discussing everything with a product manager at Salesforce Marketing Cloud I could understand the underlying problem and came up with a solution.

There are two scenarios that require different solutions as data sources and entry events differ. So if you are looking for a fix for your use case, you need to identify your problem first:

  • Scenario 1: You inject the contacts by yourself (Contact Data or API Event) and have multiple entries per contact.
  • Scenario 2: You are using a Salesforce Data Event and work on synchronized data extensions and have multiple entries per contact.

Cure for Scenario 1

This is pretty simple as fixing the evaluation problems for this kind of entry event only requires including a unique ID into your data extension and creating a custom entry event that makes use of transaction keys (which is exactly the ID we added). In fact this “wrong” evaluation isn’t a bug but rather a lack of a basic feature as buried deep down in the documentation it is mentioned that someting like transaction keys are needed for evaluation to work as expected. However the Marketing Cloud documentation on that matter is a bit outdated and as of yet doesn’t include a fully functional example and even includes some outdated code and configuration samples.

After all, if you don’t have to go through everything by trial and error caused by the lack of documentation and ignoring the boilerplate code necessary for the custom event, adding the transactionkey is as easy as this:

payload['configurationArguments'].dataExtensionId = dataExtensionId; // Id of the entry event's data extension
payload['metaData'] = payload['metaData'] || {};
payload['metaData'].isConfigured = true;
payload['metaData'].transactionKeys = {
  '0': {
    'from': transactionKeyEvent, // Column name of your transaction key in the data extension
    'to': transactionKeyDataExtension // Path of the transactionkey, which is the name of the data extension, a dot and the column name (e.g. DE_NAME.COLUMN_NAME)
  }
};

I published a full example including documentation on how to set up such an entry event under MIT license on github.com/mslabina/sfmc-customevent-with-transactionkey. Feel free to fork this project!

Cure for Scenario 2

In that case there is a confimed bug in the Contact Model which leads to the wrong evaluation. Basically right now it works like this: Journey Builder searches for the desired attribute by looking it up using the subscriber key/id and if there are multiple paths of the same length, random selection is applied. This doesn’t work out for most use cases because the lookup of a specific object (which triggered the journey) that has a many-to-one relationship to the contact cannot be looked up by the contact id alone, but rather has to be looked up by the id of this very object. So even if there is only one shortest path to an attribute the evaluation using the current algorithm would fail. I have been assured that Salesforce is aware of this and currently working on a solution that lets you choose the evaluation path. However, this won’t be released before end of 2017.

The solution until then is as simple as that: Do the evaluation yourself!

To get a bit more specific: you need to create a custom journey builder activity that is used in place of the (failing) decision split. This custom split activity takes the salesforce id of the object you need to check as input parameter and the backend of the activity then checks the current state of the desired object in your sales/service cloud instance. After that, the split returns the desired path in the journey as a result of the rules you define. In the following code example you can see the route handler that is executed for evaluation:

// Route that is called for every contact who reaches the custom split activity
app.post('/activity/execute', (req, res) => {
  verifyJwt(req.body, Pkg.options.salesforce.marketingCloud.jwtSecret, (err, decoded) => {
    // verification error -> unauthorized request
    if (err) {
      console.error(err);
      return res.status(401).end();
    }

    if (decoded && decoded.inArguments && decoded.inArguments.length > 0) {
      let serviceCloudId;

      // TODO: Read the Service Cloud object's Id from inArguments here and
      // write it to the serviceCloudId variable

      // Call the function that retrieves desired data from Service Cloud
      sfdc.retrieveFieldOfObject(serviceCloudId, (err, fieldValue) => {
        if (err) {
          console.error(err);
          return res.status(500).end();
        }

        // Check the returned value to make the decision which path should be
        // followed and return the branchResult accordingly.
        if (fieldValue === '<FIELD VALUE THAT LEADS RESULT TO PATH 1>') {
          return res.status(200).json({branchResult: '<KEY FOR PATH 1>'});
        } else {
          return res.status(200).json({branchResult: '<KEY FOR PATH 2>'});
        }
      });
    } else {
      console.error('inArguments invalid.');
      return res.status(400).end();
    }
  });
});

For the full code, please check out github.com/mslabina/sfmc-servicecloud-customsplit where I published a full example (under MIT license) including documentation on how to set up such a custom service cloud split activity. Feel free to fork this project!