A very important aspect of a CDP is it's realtime abilities. What better way to demonstrate that than by personalizing a website?
In this article we will describe how to setup a demo e-commerce site and use the FlowStack CDP to personalize some content. The "Implementors/Integrators" among our readers, will probably notice similarities with how they are achieving the same today, just using different technology stacks. Hopefully they will also see how to easily adapt to the directness of our approach.
Preparations
The site
We want something looking real with lots of sample content, so we've decided to download the Smart Bazaar e-commerce template from W3layouts and put it up on a web server.
Disclaimer: We are not affiliated in any ways with W3layouts or the creator and copyright holder of the template. The template is free for both personal and commercial use as long as attribution is not removed, and seemed to serve the purpose well for setting up personalization on an e-commerce site in 10 minutes. Finding the template is included in the 10 minutes ;) Setting up basic visitor tracking We will only implement the FlowStack jstool.js script on the index.html and the signup.html pages. (We only use the index and the signup page for this demo)
// In the head
<script>
(function(f, l, o, w, s, t, a, c, k) {
f['FS_CORE_NAME'] = w; f['FS_CORE_DOMAIN'] = s; f['FS_QUEUE'] = [];
f['fs'] = function(){f['FS_QUEUE'].push(arguments);}
a = l.createElement(o);
c = l.getElementsByTagName(o)[0];
a.async = 1;
a.src = document.location.protocol + '//' + s + '/' + t;
c.parentNode.insertBefore(a, c);
})(window, document, 'script', 'fs', 'jstool.flowstack.com', 'jstool.js')
</script>
<script src="https://jstool.flowstack.com/segment.js"></script>
<script src="https://jstool.flowstack.com/content.js"></script>
// Before the closing of the body tag
<script>
fs('init','<FLOWSTACK_ACCOUNT_DOMAIN_ID>');
fs('pageview');
</script>
We want to be able to recognize profiles (visitor -> profile relation) from the moment they are created (without having them interact with an email first) - so we will provide the visitor id with the signup form (could also be done during checkout etc).
On the signup.html page we modify the form (we want to store firstname, lastname and email) and also add a hidden input field to it, which will contain the visitor id. In the bottom of the page we use the jstool method getVar to retrieve the visitor id (the fingerprint variable) and assign it to the hidden element in the form.
<!-- The form -->
<form action="<PATH_TO_SUBMIT_SCRIPT>" method="post">
<input type="text" class="user" name="firstname" placeholder="Enter your first name" required="">
<input type="text" class="user" name="lastname" placeholder="Enter your last name" required="">
<input type="text" class="user" name="email" placeholder="Enter your email" required="">
<input type="hidden" id="visitorprop" name="visitor" value="">
<input type="submit" value="Sign Up ">
</form>
<!-- Assigning the visitor id after initializing the js tool and making standard page tracking -->
<script>
fs('init','<FLOWSTACK_ACCOUNT_DOMAIN_ID>');
fs('pageview');
fs('getVar', 'fingerprint', function(res) {document.getElementById('visitorprop').value=res});
</script>
Schema and segments
The webshop has the following categories: Electronics Fashion Photo & Gifts Home Decor Sports
Webshop customers and email subscribers will provide email, firstname and lastname
To keep it simple we create a simple schema in FlowStack to store these properties on profiles. We also create a property called ecomcategory which will be used to store a profiles preferred category (Could be assigned in multiple ways in real world applications, often as a result of behavioral analysis, AI etc)
The profile schema
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "profiles",
"description": "Profile Schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "ID"
},
"firstname": {
"type": "string",
"description": "First Name"
},
"lastname": {
"type": "string",
"description": "Last Name"
},
"email": {
"type": "string",
"description": "Email"
},
"ecomcategory": {
"type": "string",
"description": "E-com Category"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"updatedAt": {
"type": "string",
"format": "date-time"
}
}
We create 5 segments, which represents profiles of each website category. It might not be very sophisticated, but emulates quite well how most web site personalization is done today (matching tagged visitors against tagged content). The segment definitions are thus very simple.
Example segment Ecom Home:
{
"$and": [
{
"$and": [
{
"profiles.ecomcategory": {
"equals": "home"
}
}
]
}
]
We create a small script to collect signups from the signup.html page and store profiles in FlowStack. It's simply just a few lines of code which uses the REST API to check if a profile exists with the provided email. If the profile exists it is updated, otherwise it is created. We finally assign the profile id to the visitorid we inject to the signup form.
Based on your preferred backend you would do something along the lines of this:
// Looking up profile
curl -X GET \
'https://api.app.flowstack.com/v1/profiles?identifier={%22email%22:%22<SIGNUP_EMAIL>%22}' \
-H 'Authentication: <TOKEN>' \
-H 'Content-Type: application/json' \
// Save profile
curl -X POST \
https://api.app.flowstack.com/v1/profiles \
-H 'Authentication: <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"firstname": "Rune",
"lastname": "Viem",
"email": "rune@flowstack.com",
"ecomcategory": "sports"
}'
// Assign visitor to profile
curl -X POST \
https://api.app.flowstack.com/v1/visitors/<VISITOR_ID>/<PROFILE_ID> \
-H 'Authentication: <TOKEN>' \
-H 'Content-Type: application/json' \
Let the fun begin
Now we have setup the site, implemented the visitor tracking and also assign visitor ids to signed up profiles, we can start personalizing the website.
Showing featured products for the category the profile belongs to
Instead of showing the featured products for the first website category by default, we will use the jstool ask the CDP on page load if the visitor belongs to the segments which represent the website categories. Based on the result we will have the category automatically selected and the corresponding featured products shown.
On the index.html page we will add a few lines of javascript after the jstool initialization.
<script>
fs('init','<FLOWSTACK_ACCOUNT_DOMAIN_ID>');
fs('pageview');
fs('inSegment',['<SEGMENT_ID_SPORTS>','<SEGMENT_ID_ELECTRONICS>','<SEGMENT_ID_FASHION>','<SEGMENT_ID_PHOTO>','<SEGMENT_ID_HOME>'],function(result){
// Sports
if (result.result['<SEGMENT_ID_SPORTS>']) {
$('.bs-example-tabs a[href="#sports"]').tab('show');
}
// Electronics
if (result.result['<SEGMENT_ID_ELECTRONICS>']) {
$('.bs-example-tabs a[href="#electronics"]').tab('show');
}
// Fashion
if (result.result['<SEGMENT_ID_FASHION>']) {
$('.bs-example-tabs a[href="#fashion"]').tab('show');
}
// Photo
if (result.result['<SEGMENT_ID_PHOTO>']) {
$('.bs-example-tabs a[href="#photo"]').tab('show');
}
// Home Decor
if (result.result['<SEGMENT_ID_HOME>']) {
$('.bs-example-tabs a[href="#home"]').tab('show');
}
});
</script>
The above will result in real time (VERY FAST, depending on internet connection it's in the 20-30ms range) determination of which content to show based on segments.
Changing the value of the ecomcategory property of the profile, different category will be shown as default.
Rendering content with the visitors profile data
How about changing the top carousel and put in some profile data? In FlowStack you can store content snippets, which you can retrieve using the jstool. A snippet consists of a default part which is shown if no profile is assigned, and a part which is rendered with the profile's data.
Let's create a snippet, which contains the html content of the top carousel as default and a personalized carousel built with handlebars code to be shown for a known visitor (a profile)
curl -X POST \
https://api.app.flowstack.com/v1/contents \
-H 'Authentication: <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"content": "<div class=\"carousel-inner\" role=\"listbox\">{{#eq profile.profile.ecomcategory \"sports\"}}<div class=\"item active\"><!-- First-Slide --><img src=\"images/sports-photo.jpg\" alt=\"\" class=\"img-responsive\" /><div class=\"carousel-caption kb_caption kb_caption_right\"><h3 data-animation=\"animated flipInX\">Flat <span>50% </span> Discount If Your Name Is {{profile.profile.firstname}}</h3><h4 data-animation=\"animated flipInX\">Hot Offer In Sports Category Today Only</h4></div></div>{{/eq}}{{#eq profile.profile.ecomcategory \"fashion\"}}<div class=\"item{{#eq profile.profile.ecomcategory \"fashion\"}} active{{/eq}}\"> <!-- Second-Slide --><img src=\"images/fashion-photo.jpg\" alt=\"\" class=\"img-responsive\" /><div class=\"carousel-caption kb_caption kb_caption_right\"><h3 data-animation=\"animated fadeInDown\">Our Latest Fashion Editorials</h3><h4 data-animation=\"animated fadeInUp\">Special Offer If Your Name Is {{profile.profile.firstname}}</h4></div></div>{{/eq}}<div class=\"item {{#not (eq profile.profile.ecomcategory \"sports\")}}{{#not (eq profile.profile.ecomcategory \"fashion\")}}active{{/not}}{{/not}}\"><!-- Third-Slide --><img src=\"images/electronics-photo.jpg\" alt=\"\" class=\"img-responsive\"/><div class=\"carousel-caption kb_caption kb_caption_center\"><h3 data-animation=\"animated fadeInLeft\">End Of Season Sale</h3><h4 data-animation=\"animated flipInX\">Good Deals On Electronics This Week Only</h4></div></div></div>",
"fallback": "<div class=\"carousel-inner\" role=\"listbox\"><div class=\"item active\"><!-- First-Slide --><img src=\"images/img1.jpg\" alt=\"\" class=\"img-responsive\" /><div class=\"carousel-caption kb_caption kb_caption_right\"><h3 data-animation=\"animated flipInX\">Flat <span>50%</span> Discount</h3><h4 data-animation=\"animated flipInX\">Hot Offer In Sports Category Today Only</h4></div></div><div class=\"item\"> <!-- Second-Slide --><img src=\"images/img2.jpg\" alt=\"\" class=\"img-responsive\" /><div class=\"carousel-caption kb_caption kb_caption_right\"><h3 data-animation=\"animated fadeInDown\">Our Latest Fashion Editorials</h3><h4 data-animation=\"animated fadeInUp\">cupidatat non proident</h4></div></div><div class=\"item\"><!-- Third-Slide --><img src=\"images/img3.jpg\" alt=\"\" class=\"img-responsive\"/><div class=\"carousel-caption kb_caption kb_caption_center\"><h3 data-animation=\"animated fadeInLeft\">End Of Season Sale</h3><h4 data-animation=\"animated flipInX\">Good Deals On Electronics This Week Only</h4></div></div></div>"
}'
If a profile is assigned to the current visitor id, the snippet will be rendered using the profile's data.
To insert the snippet on the index.html page we just have to insert a data-fs-content attribute to the html element containing the carousel.
<div id="kb" class="carousel kb_elastic animate_text kb_wrapper" data-ride="carousel" data-interval="6000" data-pause="hover" data-fs-content="FS_CONTENT_ID">
The snippet we've created will not only decide on which parts to render (based on a comparison on the ecomcategory property), but also output actual profile data in the content.
E-commerce site in action
If you want to see an implementation of the above, please visit https://cookbook.flowstack.com/ecomdemo
If you have questions or just want to discuss challenging or exciting use cases of marketing automation and realtime personalization let us know at product@flowstack.com.