Skip to main content

SFMC Behavioral Triggers

Win-back your e-commerce customers with SFMC Behavioral Triggers.

Marketing Cloud Behavioral Triggers

Behavioral Triggers are a solution for smoothly moving abandoned engagement data from your e-commerce to Salesforce Marketing Cloud. They enable powerful win-back communication you can create with minimal technical knowledge in express time.

Currently, there are three scenarios available:

  1. Abandoned Cart
  2. Abandoned Browse
  3. Abandoned Wishlist

In each case, what Behavioral Triggers does is push relevant data (items left in the cart, browsed without conversion or added to wishlist) to the Einstein Backend and - after configured time - to Marketing Cloud Data Extension.

You can then leverage this data extension as both Journey Entry and a source of personalisation using built-in Behavioral Trigger Content Block. The latter allows you to use clean UI to configure dynamic content using the obtained engagement data.

Behavioral Triggers Pre-Work

Before you use the Behavioral Triggers, be sure to fulfil pre-requisites:

  1. As Behavioral Triggers are extending the Einstein Recommendations, get a license and enable it in your Business Unit.
  2. Many data leveraged by Triggers is coming from the Product Catalog, so be sure to configure it before capturing the events.
  3. Behavioural data is saved only for records that have Einstein Profile (IGO_PROFILE), so you need to enable Einstein Data Extensions.
  4. Implement the Collect.js script on your e-commerce to capture session and engagement data.
  5. Finally, configure Behavioral Triggers to bring relevant data to Marketing Cloud Data Extensions.

As you can see, if you already use Einstein Recommendations, most of the necessary work is already done, and full implementation should take you just a few minutes. In such a case, you will only need to update Collect.js and configure the Behavioral Triggers slightly.

How the Behavioral Triggers work

Behavioral Triggers seems to be easy to implement, but a lot is going in the backend. It is good to understand its basics, as it might help a lot when triaging a problem or customising the solution.

  1. A customer enters your e-commerce site - you trigger first Collect.js scripts to capture the Session and Page View.
  2. Customer clicks on a product page - you trigger the Page View data layer with a link to that product (Abandoned Browse scenario).
  3. Customer finds an exciting product and adds it to the wishlist - you trigger the Wishlist data layer (Abandoned Wishlist scenario).
  4. A customer decides on one of the products and adds it to the cart - you trigger the Cart data layer (Abandoned Cart scenario).
  5. Customer goes through the purchase process and buys the product - you trigger the Cart data layer with explicit parameter (This blocks Abandoned Cart from launching communication).
  6. Each of the above triggers is sent to Einstein Backend (IgoDigital) that stores the information.
  7. Marketing Cloud Behavioral Triggers query the Einstein Backend for any events that occurred some time ago - you can configure it in the range from 15 minutes to 3 hours (in 15 minutes increments).
  8. If there is a matching event, it checks whether there is any other event that overwrites it. For example, if your customer viewed a product page, and a few minutes later, added this product to the cart, the page view will be ignored. The Behavioral Trigger will wait for the abandoned cart.
  9. Once there is an event ripe for reengagement, Behavioral Trigger will put it into a system created Data Extension (one per scenario) with three data points: subscriber key, timestamp and encrypted data.
  10. Now you can use this to add the customer to a Customer Journey.
  11. In the Journey, you can use an Email with a built-in Behavioral Trigger Content Block. At the moment of sending, this block makes a call to Einstein Backend (IgoDigital) and gets the real-time state of your customer interaction. It validates that he or she did not convert to purchase in the meantime and provides all the details required for creating personalised content in the email presenting the abandoned products.

As you can see, a lot is happening, and it's time to dive deeper into each step to understand it better.

You Should Know

There are many quirks and tricks - read on to learn how to get more out of Behavioral Triggers.


Marketing Cloud Configuration

Before you implement the Collect.js, you have to make the basic configuration of the Behavioral Triggers.

Email Recommendations Configuration

If you haven't yet configured the Einstein Email Recommendations, you will have to do it first. Go to Einstein » Einstein Overview » Email Recommendations » Admin » Implementation, and it will guide you through the steps needed.

If you already configured it previously, go to Einstein » Einstein Overview » Email Recommendations » Admin » Options and make sure you have the additional options related to Behavioral Triggers enabled as described below.

For the Behavioral Triggers, be sure to select Product Catalog implementation.

Catalog and Product Attributes

On the next screen, you will be able to select which Standard Product Attributes you want to use, plus have the option to create Custom ones. It is a crucial step, as the data you choose and create here will be the basis for the Behavioral Trigger Content Block available in the win-back email.

Just remember that those attributes are global (linked to a product, not to a particular Subscriber), so don't add here parameters that are individual (like PersonalisationString).

User and Profile Attributes

The next step allows you to configure Custom Profile Attributes. It is beneficial for general Einstein purposes, but it is also crucial for Behavioral Triggers if you want to personalise your win-back email for unknown (to Marketing Cloud) customers.

If this is something you want to do, enable Custom User Profile Attributes and add necessary ones.

You can also enable localisation here, which will allow you to present the product name in the user's language.

Activity Tracking

On this last step before Summary, you configure what you will be tracking. Category View and In-Site Search are nice to have for general Einstein features, but for Behavioral Triggers, be sure to check Cart Activity and Purchase Activity. You will need it for Collect.js.

Initial Catalog Upload

Once you configure the Email Recommendations, it is time to make the first upload of the Catalog. Go to Einstein » Einstein Overview » Email Recommendations » Admin » Catalog and click Connect a New Catalog. Select the Product Catalog, import method and format. Pass the column's name that contains Unique ID and tell SFMC whether it is complete or partial Catalog import.

Once Marketing Cloud completes the upload, be sure to map the fields from your source to the Standard and Custom Fields available in the Catalog.

Einstein Data Extension

The last step here is to enable Einstein Data Extensions. To do this:

  1. Go to Einstein » Einstein Overview » Email Recommendations » Admin » Status.
  2. Click on the cog icon in the top right and select Data Extension Settings.
  3. Enable them with a toggle.

If the toggle is inactive, you need to:

  1. Make sure your Catalog is uploaded and mapped.
  2. Make test Collect.js push to initialise Einstein. To start, you can utilise ready-to-use base script:
initialise Einstein with basic Collect.js
<script type="text/javascript" src="MID.collect.igodigital.com/collect.js"></script>
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "initialise Einstein"}]);
_etmc.push(["trackPageView"]);
</script>

Create a Cloud Page, paste the above snippet in the HTML block - update both MID's with the Business Unit you implement the Behavioral Triggers on - and publish it. After opening the website, you can delete the Cloud Page - so that it doesn't affect your future Einstein data.

You will find details on what is happening in this script in the Collect.js section below. As for now, you have to wait for the initialisation to happen. It can take a day. To check whether it is ready, try to Enable the Einstein Data Extensions again. During the initialisation, the Status page won't be loading (you will see the circle spinning infinitely).

Behavioral Triggers Configuration

Once you configure and enable the Email Recommendations, it is time to set up the Behavioral Triggers themselves. It will be simpler and easier. Head to Journey Builder » Behavioral Triggers.

First, click New Trigger in the top right. You can select from the three available abandonment scenarios:

  1. Abandoned Cart
  2. Abandoned Browse
  3. Abandoned Wishlist

Select the one you want to start with, and you will be able to set suppression rules - one related to multiple triggers and the second to a recent purchase. For initial configuration, I recommend setting both to 0. It will make testing much more manageable. Once you go to production, change those appropriately as per your business requirements.

When you confirm the setup, Marketing Cloud creates the system Data Extension to store Behavioral Trigger data (one per scenario).

The only left option is the Session Timeout Limit. You can access it by clicking the cog icon next to the New Trigger button. You can select from a range starting at 15 minutes and going in 15 minutes increments up to 3 hours. Again, for testing purposes, make it the shortest possible and later update based on business needs.

You Should Know

Currently, it is not working at all - it does not save the selected setting, and it always shows 1 hour. Until fixed, you can manage this by going to Einstein » Einstein Overview » Email Recommendations » Admin » Implementation and clicking Advanced Settings on the left pane. There will be a Session Length option with the same range. It is the same setting.


Collect.js

You have seen the Collect.js script mentioned multiple times here, and it is also the most technical elements of the standard Behavioral Triggers implementation. Time for details.

Collect Code Script

Before you start building the data layers, you first need to import the code responsible for pushing data to Einstein Backend (IgoDigital). You do this by putting this line on every page:

Collect Code Script
<script type="text/javascript" src="MID.collect.igodigital.com/collect.js"></script>

Be sure to change the MID in the above URL to the ID of the Business Unit that you want to use for your Behavioral Triggers.

Once you have it, you may start building the data layers that will move the information from your e-commerce to Marketing Cloud. For simplicity, I won't be writing the above code in the snippets below.

Set Org ID

The first data layer you need to push contains, again, the MID of the Business Unit. Just as with the Collect Code Script, be sure to change the MID to your SFMC BU's actual ID.

Set Org ID Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
</script>

Set User Info

The second data layer that will be key for sending Behavioral Triggered communication pushes the customer's data. Remember that this layer must be added to your Collect.js before any tracking layer for proper attribution.

Set User Info Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
</script>

The base function is pushing your customer's unique identifier (don't be intimidated by the email in that code - you do not have to pass the email address there). Depending on your use case, it might be either:

  1. Subscriber Key (if you plan on sending only to contacts known to Marketing Cloud or have an option to generate Subscriber straight from your e-commerce) or
  2. Email Address (recommended if you want to push win-back emails to unknown users in your Marketing Cloud and you do not have an option to create them properly.)
You Should Know

If you decide to push an Email Address to communicate with people not yet in the Marketing Cloud, be sure to make them Subscribers. You can easily do it using the classic Export-Transfer-Import Automation on the data stored in the Behavioral Triggers Data Extension. Leverage SQL Activity to copy the needed data to another Data Extension using this query:

Basic Subscriber data preparation
SELECT
GUID() AS SubscriberKey, // OR: subscriber_key AS SubscriberKey,
subscriber_key AS EmailAddress
FROM abandoned_cart_123456789

Be sure to update the name of the Behavioral Trigger Data Extension.

You can leverage the same automation to trigger the Journey Entry to ensure that all records in the Journey already exist as Subscribers.

You can provide more data in this data layer by adding Custom Einstein Profile Attributes as second parameter:

Extended Set User Info Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {
"email": "UniqueIdentifier",
"details": {"gender": "male"}
}]);
</script>
You Should Know

Remember that the snippet I shared above is just an example. You will probably use different Custom Profile Attributes. To get the snippet based on your configuration, go to Einstein » Email Recommendations » Admin » Implementation.

Be sure to create all those Custom Profile Attributes in your Einstein implementation before pushing it via Collect.js.

Catalog Update Streaming

There are two options to keep your Einstein Catalog up-to-date. Manual uploads and Catalog Update Streaming.

You Should Know

With April 2021 Marketing Cloud Release the Collect.js Streaming Updates will be deprecated. The way forward will be Streaming Updates via API. Read more here.

As existing implementation will be available for some time still to allow for migration, I'm keeping this section until the end of the grace period.

Manual uploads might be fine if you nearly never update items or their parameters in your e-commerce. For all other use cases, you should be using Catalog Update Streaming Data Layer.

Page View Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["updateItem", {
"item_type": "product",
"item": "ProductCode",
"name": "ProductName",
"description": "ProductDescription",
"url": "ProductURL",
"unique_id": "ProductSKU",
"available": "y",
"image_url": "ProductImage",
"sale_price": "ProductSalePrice",
"price": "ProductRegularPrice",
"release_date": "ProductReleaseDate",
"custom_attribute": "ProductAttribute"
}]);
</script>
You Should Know

Remember that the snippet I shared above is for a fully-fledged Catalog with one sample Custom Attribute. You may use only some of those parameters or utilise multiple custom attributes.

You can get the complete list of the parameters you need to use by going to Einstein » Email Recommendations » Admin » Implementation. If you have Streaming Updates enabled (and you should have to use this data layer) below, you will see the entire snippet adapted to your configuration.

Be sure to check also the Domain table visible above it - you should add all the domains connected to your Streaming Update, so:

  1. The domain where the Stream is happening
  2. The domain of the Product URL
  3. The domain of the Product Image URL

In many cases, all those domains might be the same, but if you are using Content Delivery Network or a different subdomain for the backend, those might differ. If you cannot click Register, be sure you pass just the domain - the bolded part of https://domain.name/folder/structure?query=string.

With this data layer, you can be sure that the Catalog stored in Marketing Cloud is always up-to-date. It is crucial, as everything in the Behavioral Trigger Content Block uses the Catalog data.

The available parameter is essential as it is the best way to make sure the personalisation in the email won't show the product that is no longer available even if it was in the abandoned cart. Of course, this is true for the moment of email send, not the moment of email open.

You can either:

  1. Add this data layer to other pushes of Collect.js as shown in the snippet above or
  2. Create a separate process on the backend of your e-commerce. It can shoot an update whenever there is a change in the product parameters.

The first approach is straightforward; the second is wise.

You Should Know that

Remember that this data layer updates the Catalog. Even if you push it in the context of a specific subscriber and along the Track Cart data layer, it will still update the global Catalog for everyone.

If your customer has a unique product price (for example, via VIP status or coupon), use the Track Cart data layer to share this information.

If you are using localisation, you may also pass the translated product name using "locale_pl_name": "PolishProductName". The Behavioral Trigger Content Block can leverage it with a trick.

Track Page View

Time for our first real tracking - Page View. The basic data layer pushes just the information about the event:

Page View Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackPageView"]);
</script>

However, you can do much more with this data layer by providing additional parameters. The most important for Behavioral Triggers is item:

Page View Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackPageView", {"item": "ProductCode"}]);
</script>

Once you swap ProductCode for the actual product code matching one of the items in Einstein Product Catalog, you will be able to leverage the first Behavioral Trigger scenario - Abandoned Browse. Once you push this event and the customer does not convert further, this will populate Abandoned Browse Data Extension and allow win-back communication.

You can also add other parameters that are not used by the Behavioral Triggers but rather standard Einstein Recommendations - category and search:

Page View Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackPageView", {
"item": "ProductCode",
"category": "ProductCategory",
"search": "SearchTerm"
}]);
</script>

You do not have to use all three; you can trigger various ones depending on the page the user is on. The floor is yours.

Track Wishlist

The second Behavioral Trigger scenario - Abandoned Wishlist - has its own data layer:

Track Cart Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackWishlist", { "wishlist": [
{"item": "ProductCode", "unique_id": "ProductSKU"},
{"item": "ProductCode", "unique_id": "ProductSKU"}
]}]);
</script>

As you can see, not much is configurable here - all of the product data come from the Einstein Catalog, so be sure to have it up to date.

Track Cart

The third Behavioral Trigger scenario - Abandoned Cart - also has its own data layer:

Track Cart Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackCart", { "cart": [
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"},
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"}
]}]);
</script>

Few things here are important to consider:

  1. As you can see, there are four possible parameters for each item - item and unique_id are required and must be available in your Einstein Product Catalog to work. The other two - quantity and price are optional.
  2. The price parameter here is different from price or sales_price available in Catalog Update. The Catalog ones are global - the same for all your customers. The price pushed within Track Cart is unique for this customer and might differ from the global ones (think VIP status or a coupon used). It won't update your Einstein Catalog.
  3. Always push the whole cart. If your customer has more than one item in it - put all of them in this data layer. If a customer deletes an item from the cart - push the data layer without the deleted one. The last version pushed is considered the final state of the cart for personalisation purposes.
  4. Always be sure to clear the Track Cart data whenever the cart data is no longer needed. If the customer purchased the products, be sure to use the Track Purchase data layer. If the customer deleted all the products from their cart, use the Clear Cart parameter:
Clear the Track Cart Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackCart", {"clear_cart": true}]);
</script>

Otherwise, your customers might receive win-back communication for products that they already bought.

Track Purchase

Whenever the customer converts, be sure to track this purchase. It is not only crucial for Einstein Recommendations and Discover's ROI calculation. For Behavioral Triggers, it is vital, as it informs that the cart converted. It blocks win-back communication for already purchased products.

The basic structure of the Track Purchase data layer is very similar to the Track Cart one:

Track Cart Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackPageView", {"item": "ProductCode"}]);
_etmc.push(["trackConversion", { "cart": [
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"},
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"}
]}]);
</script>

You can, however, extend it with additional information that will provide more context to the conversion:

Track Cart Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["setUserInfo", {"email": "UniqueIdentifier"}]);
_etmc.push(["trackPageView", {"item": "ProductCode"}]);
_etmc.push(["trackConversion", {
"cart": [
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"},
{"item": "ProductCode", "quantity": "Quantity", "price": "IndividualPrice", "unique_id": "ProductSKU"}
],
"order_number": "OrderNumber",
"discount": "DiscountAmount",
"shipping": "ShippingPrice",
"details": {"CustomAttribute": "CustomValue"}
}]);
</script>

Do Not Track

You may suppress all of the configured trackings by using a data layer based on customer decision and local laws.

Do Not Track Data Layer
<script type="text/javascript">
_etmc.push(["setOrgId", "MID"]);
_etmc.push(["doNotTrack"]);
</script>

Behavioral Trigger Content Block

Now you can go forward with Behavioral Triggers. Collect.js will push the required data to Einstein backend, and after Session Timeout Limit, it will evaluate it against Suppression Rules as per your configuration. Then, the Behavioral Triggers adds Abandoned Engagement data to the respective Data Extension. Now it's time to use it.

The standard way the leverage this information is to use those abandonment Data Extensions as a Journey Entry Source. The real magic, however, will happen in the email you will be sending in this Journey. And that is thanks to the out-of-the-box Behavioral Trigger Content Block.

To check it out, edit email, and on the bottom of the left pane containing all the Content Blocks, you will find the Behavioral Trigger one. Drag and drop it to the email so that you can start configuring it.

When you click that block after adding it to the email, you will be able to limit the maximum number of products shown, control the layout with columns, change order and decide which data points should be visible for products.

It's easy, it's quick, but there are multiple issues if you want anything a bit more customised for your needs. With the out-of-the-box solution:

  1. You don't have a straightforward way to control the style of this information. You can only go to the Styling tab of the editor and modify the CSS available there.
  2. You are limited only to the fields that you configured for the Product Catalog. For example, you cannot use the individual price you passed in the Track Cart data layer. You can vote for it on IdeaExchange.
  3. There is no information on how to access the data outside of the Content Block - but you can learn about it later in this article.
  4. You cannot leverage Einstein API's full potential, as the hard-coded request does not have a parameter responsible for pulling the Einstein Custom Profile Attributes. You can vote for it on IdeaExchange.
  5. You cannot use multiple Content Blocks pointing to various Behavioral Trigger scenarios (for example,first focus on the cart and then present the wishlist).

As far as you can fix the first issue with good CSS knowledge, the rest require more work.

So what can you do? Configure the Content Block to be as close as possible to what you want, go to Code View of your Email and look for Concat('{"trigger_payload":"', data, '"}'). It starts the code responsible for Behavioral Trigger Content Block.

Below you can see two tabs.

  1. In t Original Code, you will find the complete code of the built-in Behavioral Trigger Content Block. Shown configuration displays a product image with name and description plus regular/sale price. The number of items is limited to four in a two-column layout.
  2. In the Code Highlights tab, I focus on some part of the code that I found exciting and deserve some attention.

1. AMPScript personalisation

%%[
Set @data = Concat('{"trigger_payload":"', data, '"}')
Set @mid = memberid
]%%

The first four lines of the Behavioral Trigger Content Block code are already interesting. They are using AMPScript, although the rest is in SSJS. It could easily be written in SSJS altogether for more optimised execution.

You can also see that they are pulling the behavioural data by using a personalisation string on data - the Abandoned Engagement Data Extension column containing encrypted information. If you want to have more control over the source of this information, you can think about making a lookup here. It would allow you to even use multiple Content Blocks in one email (or mix browsed, wishlisted and added to cart items in one block).

2. Try/Catch block

try {
// Behavioral Trigger Content Block code
} catch(e) {
Write(e);
Platform.Function.RaiseError("Quit send.", true, "statusCode","3");
}

The try/catch block is a great idea, but as it has separate scope in SSJS, it means that no SSJS variable from within will be available outside of it. Thankfully the other part of the code is sharing the Behavioral Trigger data using AMPScript variables.

If you want to access the SSJS ones, you can declare them before the try/catch starts or pull them from the AMPScript.

3. Settings & Markup Fragments

var settings = {
// Content Block settings from UI
};
var markupFragments = {
// HTML fragments
};

Those two variables store most options that you have selected when configuring the Behavioral Trigger Content Block. You can use them to make configuration impossible in the UI. You can also change the HTML used for the product presentation.

Remember, however, that the Behavioral Trigger Content Block use classes visible in the markupFragments in the CSS. Changes might break the styling.

You Should Know

The current version of Behavioral Trigger Content Block is not rendering correctly on Yahoo, AOL and Windows Outlook. Learn more here.

Fix it by changing rem units in markupFragments to px ones.

Later there are also two other settings-related lines:

var useRecentItems = false;
// Some code
var useSalePricing = true;

You can also configure them within the Behavioral Trigger Content Block UI. Respectively, by checking either:

  • "If the subscriber's abandoned items have changed since the triggering event, include the updated items" or
  • "Only show sale price if sale price is > 0"

Both checkboxes are available in the Product Fields section. The second one, however, is visible only if you add the sale_price field.

If you don't enable the useRecentItems, the Content Block will take the products initially pushed with Track Cart Collect.js. If you use it, it will instead take the latest data available that, in some cases, might be either bigger (if your customer added few items) or smaller (if he deleted some or just cleared the whole cart). There is also a block that stops the send if there are no more items in the current cart.

As for useSalePricing, it is much less straightforward. What "Only show sale price if sale price is > 0" enabled does is check whether there is sale price and whether it is lower than the regular price and - if yes - it adds strike-through style to the regular price — end of the story. For anything more attractive, you will need to play with custom CSS and maybe additional SSJS logic.

4. Pulling data

var data = Platform.Variable.GetValue("@data");
var mid = Platform.Variable.GetValue("@mid");
var event_locale = Platform.Variable.GetValue("@event_locale");

Two first lines here are pulling data from the AMPScript described in the first highlight. As mentioned, it can be optimised by making the personalisation here and removing AMPScript altogether.

More interesting, however, is the third line. You can configure localisation for your products in Email Recommendations Configuration, but it is not used by the Behavioral Triggers - officially. Thanks to this little line, you can - even with the built-in standard Content Block - declare the locale source for the Subscriber and set it to @event_locale AMPScript variable. Bam, your Behavioral Triggers are now localised.

5. Building the API query

var protocol = "https";
var hostname = mid + ".recs.igodigital.com";
var qs = "?item_count=" + settings["maxItems"] + "&sort_by=" + settings["sortBy"] + "&sort_direction=" + settings["sortDirection"];
var includes = [];
for (var key in settings["fields"]) {
if(defaults[key] == null) {
includes = includes.concat(settings["fields"][key]);
}
}
if(includes.length > 0) {
qs += "&include=" + includes.join("|")
}
if(event_locale){
qs += "&locale=" + event_locale
}
var url = protocol + "://" + hostname + "/" + mid + "/trigger" + qs;

The above snippet is responsible for building the URL that is used for an HTTP.Post API call to Einstein Backend (IgoDigital). You can make a simple addition that will enable you access to Einstein Custom Profile Attributes. Must-have if you want to boost engagement with personalisation for new customers.

To do so, just before the line highlighted in the snippet above add a new one with:

qs += "&user_attributes=CustomProfileAttribute1|CustomProfileAttribute2"

Just change the values after the equal sign to the exact names of your Einstein Custom Profile Attributes that you want to have available in your code (separated by |).

6. Response parsing

for(var responseField in response) {
var value = response[responseField];

if(responseField === "products" || responseField === "current_cart") {
var prefix = (responseField === "products") ? "@item_" : "@cart_";
for(var i=0; i<value.length; i++) {
for(var itemField in value[i]) {
var name = prefix + itemField + "_" + (i+1);
var val = value[i][itemField];
Platform.Variable.SetValue(name, val);
}
}
}
else if(responseField === "user") {
var prefix = "@user_";
for(var userField in value) {
var name = prefix + userField;
var val = value[userField];
Platform.Variable.SetValue(name, val);
}
}
else {
Platform.Variable.SetValue("@"+responseField, value);
}
}

The code you see above is responsible for reading the response from Einstein Backend (Igodigital). It goes through each element of the response and creates a separate AMPScript variable with an appropriate value.

  1. For each element of the products key, it creates a variable build with item_ prefix, value name and _X suffix, where X is the counter of the item (so 1 for the first item, 2 for the second and so on). It means you can get the first link to the first item by calling the @item_link_1 variable. To get the name of the second item goes with @item_name_2.
  2. If the response contains the current_cart key (it is available only if the Track Cart was triggered), it also creates variables for it. The structure is nearly the same - just the prefix changes to cart_. The most compelling use case here? You can get the price information that you pushed via Track Cart (the one unique to a specific cart, not coming from the Product Catalog) by calling @cart_amount_1 for the first item.
  3. There is even a system that gets Custom Profile Attributes as AMPScript variables! It uses user_ prefix, so if you want to get, for example, the gender - call @user_gender variable.
  4. All other keys of the response are also created with just their name. So to get information on whether the cart converted, you can call @purchased variable.

To check what is available to you, check example API responses coming from Einstein Backend.

You Should Know
  1. The current_cart key and the amount variable is available only for the Abandoned Cart Behavioral Trigger. Wishlist and Browse only contain products information coming from the Product Catalog.
  2. Even though there is already a code that parses Custom Profile Attributes, it won't give you anything on the built-in standard Behavioral Trigger Content Block. It won't have the user key populated. You must enable each attribute in the API call.
  3. If you want to use more than one Abandonment Engagement in one email, you will have to alter the prefixes in this code not to overwrite the AMPScript variables.

Those AMPScript variables are a must if you want to create a Call to Action button that will rebuild the cart in your e-commerce. Remember, however, that it is custom development without any out-of-the-box snippets from Salesforce.

The rest of the code is just taking care of the proper display of the information in the email and stopping the send with RaiseError if the customer purchased the cart or the API did not respond. It is added to the email using %%=treatascontentarea('BT Trigger', @content)=%%.

API Responses

To fully leverage the Behavioral Trigger Content Block's customisation options, you can check the sample responses from Einstein Backend (IgoDigital) available below. They will help you know what AMPScript variables can be available after the Content Block.

{
"products": [
{
"link": "https://123456789.collect.igodigital.com/redirect/UNIQUE_CODE",
"image_link": "https://your.product/image.png",
"product_code": "ProductCode002",
"name": "ProductName2",
"regular_price": 88,
"sale_price": 55,
"sku_id": "SKU2",
"online_availability": "Y",
"quantity": 1,
"amount": "44.00"
},
{
"link": "https://123456789.collect.igodigital.com/redirect/UNIQUE_CODE",
"image_link": "https://your.product/image.png",
"product_code": "ProductCode001",
"name": "ProductName1",
"regular_price": 99,
"sale_price": 66,
"sku_id": "SKU1",
"online_availability": "Y",
"quantity": 1,
"amount": "33.00"
}
],
"current_cart": [
{
"link": "https://123456789.collect.igodigital.com/redirect/UNIQUE_CODE",
"image_link": "https://your.product/image.png",
"product_code": "ProductCode001",
"name": "ProductName1",
"regular_price": 99,
"sale_price": 66,
"sku_id": "SKU1",
"online_availability": "Y",
"quantity": 1,
"amount": "33.00"
},
{
"link": "https://123456789.collect.igodigital.com/redirect/UNIQUE_CODE",
"image_link": "https://your.product/image.png",
"product_code": "ProductCode002",
"name": "ProductName2",
"regular_price": 88,
"sale_price": 55,
"sku_id": "SKU2",
"online_availability": "Y",
"quantity": 1,
"amount": "44.00"
}
],
"same_products": "Y",
"user": {
"gender": "Male"
},
"purchased": false,
"abandoned_items": "SKU2|SKU1",
"tracking_pixel": " "
}
You Should Know
  1. You can see that there is "gender" data in the "user" key in the example above. You will see information here only if you alter your API call. Otherwise, it will be empty.
  2. The amount key is available in both products and current_cart keys, but it is in a string format. Other prices (regular_price and sale_price) are integers.
  3. In same_products, you will have information ("Y"/"N") whether the Cart content changed between the moment you pushed this data and the API call.

Debugging Behavioral Triggers

There are three critical steps of Behavioral Trigger implementation when you might need to debug your solution. As you will be merging multiple systems and working on time-sensitive data using a new feature, look behind the curtain will be crucial. Thankfully, there are few options that we can use.

Debugging Collect.js

The first thing to check is your frontend Collect.js implementation. To quickly review whether Collect.js is available on your website, open Developer Tools in your browser and go to the Console tab. In there, write _etmc.

If you see ReferenceError: _etmc is not defined, it means that the script is not available. Ensure that you are checking a page that is supposed to have Behavioral Triggers implemented and that the script is added correctly.

If the script is implemented, you will see an Object containing over a dozen of keys. Many of which are the functions that pass the data from your website to IgoDigital.

Now you can leverage its power by writing in the console _etmc.debug = true to enable debugging mode of Collect.js. Once set, it will log every change applied to the dataLayer. Make a tracked action - like adding a product to a cart - to see it work.

Example data logged after enabling debug and adding product to the cart
{
"cart": [
{
"item": "2020572",
"unique_id": "2020572AEUAEL",
"name": "Women’s Red Box Pullover Hoodie",
"url": "https://production-internal-ccdemo.demandware.net/s/NTOSFRA/default/women%E2%80%99s-red-box-pullover-hoodie-2020572AEUAEL.html",
"price": 50,
"sale_price": 50,
"item_type": "product"
}
],
"url": "https://production-internal-ccdemo.demandware.net/s/NTOSFRA/default/women%E2%80%99s-red-box-pullover-hoodie-2020572BHX.html?source=igodigital"
}
You Should Know

You can check how Collect.js works by reviewing its contents. To do this, go to the link contained in your base Collect code. It should be: http://YOUR_MID.collect.igodigital.com/collect.js.

Another option to observe what Collect.js is sending to Einstein IgoDigital backend is to go to Network tab of the Developer Tools and look for calls made to igodigital.com address. You will see the above data in form of API request payload:

https://stack7.collect.igodigital.com/c2/northerntrailoutfitters/track_cart?payload={"cart":[{"item":"2020572","unique_id":"2020572AEUAEL","name":"Women’s Red Box Pullover Hoodie","url":"https://production-internal-ccdemo.demandware.net/s/NTOSFRA/default/women%E2%80%99s-red-box-pullover-hoodie-2020572AEUAEL.html","price":50,"sale_price":50,"item_type":"product"}],"url":"https://production-internal-ccdemo.demandware.net/s/NTOSFRA/default/women%E2%80%99s-red-box-pullover-hoodie-2020572BHX.html?source=igodigital"}

Using either of those two tools will allow you to make sure that:

  1. You have correctly implemented the Collect.js script.
  2. It is correctly assigned to the website events you want to track.
  3. It captures necessary information, like price, name of the product, id.
  4. It passes the correctly structured JSON with this data to IgoDigital.
  5. You receive status 200 on the API call in the Network tab.

Once all of the above is validated, the correct data should be available to Salesforce Marketing Cloud Einstein and Behavioral Triggers.

Debugging Data Flow

Now it's time to check whether the data is coming through to your Salesforce Marketing Cloud.

There are few things to check:

  1. Go to Journey Builder » Behavioral Triggers and check whether there is a required Trigger Type available with Active status.
  2. Go to the Status tab and check whether the Status next to Collect Tracking Code Type is Ok. Don't worry about the numbers in Collected Today - those are frequently not aligned with reality.
  3. Go to Audience Builder » Contact Builder » Data Extensions and in the main local folder look for IGO_PRODUCTS and IGO_PROFILES Data Extensions. You should see the captured users and products details. It validates that the data is flowing through Collect.js.
  4. Finally, it's time to check the Data Extension related to the Behavioral Trigger Type you have activated. It should be in the same main local folder, named abandoned_TYPE_01234 (with TYPE being wishlist, cart or browse depending on what you enabled).

Remember that the Behavioral Trigger data will be delayed by the amount of time configured during the setup (between 15 minutes and 3 hours). For debugging purposes, the shorter, the better.

If you do not see data in the above places - the most probable cause is Collect.js script implementation. Go to the previous debugging step and check whether everything is set up correctly. If you are confident that all is good and after waiting the required amount of time, contact Salesforce Support for help.

Debugging BT Content Block

Once you complete the above steps, it's time to play with Behavioral Trigger Content Block.

The easiest way to check whether everything works fine is by adding the default one and checking whether the data makes sense. The hard part starts when there is an issue with the outcome (incorrect data, missing data or just no preview).

I use a simple script to find what might be the source of the issue:

<script runat="server">

Platform.Load('Core', '1');

try {
var defaults = {
'link' : 'Link',
'image_link' : 'ImageLink',
'product_code' : 'ProductCode',
'name' : 'Name',
'regular_price' : 'RegularPrice',
'sale_price' : 'SalePrice',
'sku_id' : 'SkuID'
};

// Data from Marketing Cloud
var data = Platform.Variable.GetValue('@data');
var mid = Platform.Variable.GetValue('@mid');
var subKey = Attribute.GetValue('_subscriberkey');
var email = Attribute.GetValue('emailaddr');

// Data from block settings
var settings = {'fields':{'image_link':'ImageLink','name':'ProductName','regular_price':'RegularPrice','quantity':'quantity'},'maxItems':3,'sortBy':'item_order','sortDirection':'desc','desktopCols':1,'mobileCols':0,'useSalePricing':false};

// Build the url
var protocol = 'https';
var hostname = mid + '.recs.igodigital.com';
var qs = '?item_count=' + settings['maxItems'] + '&sort_by=' + settings['sortBy'] + '&sort_direction=' + settings['sortDirection'] + '&user_attributes=gender';
var includes = [];
for (var key in settings['fields']) {
if (defaults[key] == null) {
includes = includes.concat(settings['fields'][key]);
}
}
if (includes.length > 0) {
qs += '&include=' + includes.join('|')
}
var url = protocol + '://' + hostname + '/' + mid + '/trigger' + qs;

// Call IdoDigital Backend
var result = HTTP.Post(url, 'application/json', data, []);
if (result.StatusCode == 200) {
var response = Platform.Function.ParseJSON(result.Response[0]);
}

// Print it in the Email
Write('Your Subscriber Key: ' + subKey + '<br>')
Write('Your Email: ' + email + '<br>')
Write('Your Data: ' + data + '<br>')
Write('Your Items: ' + Stringify(response) + '<br>');

} catch(e) {
Write(e);
Platform.Function.RaiseError('Quit send.', true, 'statusCode','3');
}
</script>

It is a very basic script but helps with finding the source of an issue. As you can see, a lot of it is just a copy-paste of the original Behavioral Trigger Content Block. But instead of displaying a beautiful abandoned cart message, it will show you Subscriber Key, Email Address, raw data that is being shot to IdoDigital Backend and the response from it.

Using that information, you can validate whether the raw data, email address and subscriber key belong to the same Contact and whether the response is aligned with what you were pushing during your testing on the website.

If there is anything wrong with that information, check whether you have a correct Collect.js implementation, whether it captures the accurate product information and assigns it to the right Contact data.

If the response is correct, but there are still some issues with what the Behavioral Trigger Content Block displays - check Content Block settings. If something is still off - welcome to the new adventure - a Custom Behavioral Trigger Content Block creation.