Server Side JavaScript

Date formats and time zone can be a tricky thing to manage in Marketing Cloud due to how each studio handles and displays date time information; I have spent more than my fair share of time testing formats and time zones to ensure the correct date is captured. So here are my tips and technical documentation of how to navigate this part of the platform.

What time zone is Marketing Cloud in?

The Salesforce Marketing Cloud servers operate in Central Standard Time (CST), also known as GMT-6. This means it’s 6 hours behind GMT+0, UTC and “Zulu” time. If it’s midday in Greenwich (UK), then it’s only 6am in CST; 6 hours behind. All data and programming languages will operate with CST as the server time, so you have to account for this in your code!

What time zone should I store Marketing Cloud data in?

Not really a clean cut answer here – store you customer/business data as you need to use it. However I recommend storing data in the server time (CST, GMT-6) so that it’s accurate at the data level. If you know the underlaying data is correct, then you can choose how to display it with some simple formatting tricks. Attempting to store date data in your local time zone may look good on the surface, but you’re going to run into issues later when you try and use SSJS or SQL to compare dates.

What date formats does Marketing Cloud accept?

Pretty much all of them… Marketing Cloud is built to handle all the standard localisations of date formats. The date format setting on file imports gives you some more direct control over this. However if you are looking for the “Best” formats to use when importing data into Marketing Cloud – here is my recommendations:

  • YYYY-MM-DD hh:mm:ssZ (eg: 2021-07-14 12:05:13Z, where Z indicates GMT+0, aka Zulu time)
  • YYYY-MM-DDThh:mm:ssZ (eg: 2021-07-14T12:05:13Z, where Z indicates GMT+0, aka Zulu time)
  • YYYY-MM-DD hh:mm:ss+##:00 (eg: 2021-07-14 12:05:13+10:00, where +10 indicates GMT+10)
  • YYYY-MM-DDThh:mm:ss+##:00 (eg: 2021-07-14T12:05:13+10:00, where +10 indicates GMT+10)
  • YYYY-MM-DD hh:mm:ss.SSSZ (eg: 2021-07-14 12:05:13.000Z, where Z indicates GMT+0, aka Zulu time)
  • YYYY-MM-DDThh:mm:ss.SSSZ (eg: 2021-07-14T12:05:13.000Z, where Z indicates GMT+0, aka Zulu time)
  • YYYY-MM-DD hh:mm:ss.SSS+##:00 (eg: 2021-07-14 12:05:13.000+10:00, where +10 indicates GMT+10)
  • YYYY-MM-DDThh:mm:ss.SSS+#:00 (eg: 2021-07-14T12:05:13.000+10:00, where +10 indicates GMT+10)

The above formats are all subsets of the ISO 8601 international date format, and are the most command (and least ambiguous) ways to represent datetimes. Try to use one of these formats when bringing data into Marketing Cloud to ensure it’s being handled correctly on import.

How can I convert DateTime data in Marketing Cloud?

There are a few ways you can display datetime values in Marketing Cloud.
If you are looking for a way to print your datetime data on a CloudPage or Email, then you can use AMPscript functions like Format() and FormatDate(), or SSJS Functions like Format() or Get Date Parts. You can also use string alteration functions to manually recreate any date format you need.

For SQL date in Marketing Cloud you can use functions like Convert(), Cast() and DatePart(). You can also assemble your own custom date format using Concatenation and string alteration functions if needed (although not recommended).

How can I convert time zones in Marketing Cloud?

Offt, this one is not as easy.
The only time zone converter you have out of the box is the “Server to Local Time Zone” functions in AMPscript, SSJS and SQL. This can be really useful for taking your data stored in Server Time GMT-6 and parsing it into Local Time, however there are a few known quirks with these functions. One such quirk is that it will convert the actual date time (the date & hours) however it wont update the GMT to the correct value, which can cause downstream issues for anyone receiving your data.

You can play around with the FormatDate() (and similar) functions to produce a usable time zone converted date, however you’re likely going to need some string alteration to clean it up a bit. My preferred way is to manually add/remove hours from the date object. Given the server is in CST (GMT-6), you can easily add 6 hours to that time (AMPscript, SSJS & SQL) to enforce Zulu time. From here you can safely export the “YYYY-MM-DD hh:mm:ssZ” format or use a data formatter to convert the Zulu timestamp to a localised one.

Read more

Once upon a time there was a neat trick that SFMC Developers could use to quickly write and run code in Marketing Cloud. You could create a Cloud Page Landing Page, write your code, and press Preview – this would quickly run the code without publishing the Cloud Page or consume any Super message credits.

However a recent update to how Cloud Pages handles programmatic code (specifically SSJS) has made this rapid development method inoperable. Marketing Cloud also has a 5 minute publishing window for changes made to Cloud Pages, so simply updating your code and republishing it can be a very time consuming activity.

Luckily, there is still a workaround that you can use to quickly build and test your programmatic code!

Use Cases

Working with large blocks of code or 3rd party APIs can sometimes take some trial and error to get right, so unit testing with text outputs can really speed up development. I’ve also had tasks that you just need to process once – such as separating email addresses from their email domain – and having a quick workbench to write the necessary script and run it once can save you a lot of hassle.

How it works

In Content Builder, create a HTML block and paste the programmatic code you want to test. Save the Content Block and copy the Content Block ID from Content Builder.
Next, create a new Cloud Page, and insert the ContentBlockbyId() AMPscript function, pasting the Content Block ID from your saved HTML Block.
Save and Publish the Cloud Page.

Content Block containing some SSJS code.
Cloud Page using ContentBlockbyId() to reference the SSJS Content Block

Now you can access the Cloud Page via it’s published URL. Each time you access the Cloud Page, it will make a dynamic lookup to the HTML Content Block, meaning you can update the content block and refresh the Cloud Page to see changes immediately!

The drawback to this method is that it does consume a Super message credit each time the Cloud Page is reloaded. Depending on the code you are testing and the messages/details you want to print on the page, you can use one of the Code Resource page types to get around the super message cost, however be carful of which code page type you choose as they each contain different headers which could affect your testing.

Conclusion

There are loads of other ways that members of the SFMC community have found – including using Github repositories – to speed up development in Marketing Cloud. See what works for your development style, but always check if your solution introduces any security or utilization concerns!

Read more

One of my most common use cases for SSJS is making API calls in Automation Studio using the Script Activity. Building an automation that can retrieve or refresh data from an external source can open up so many amazing communication opportunities.

You could get the local weather data feed and use it to advertise weather appropriate products or destinations – I actually did a video on this here: https://www.youtube.com/watch?v=vCSCnoiR5ww
You could get up to date price data on hotels or flights to identified cheap travel opportunities; or even query the daily movement of bitcoin for your subscribers!

However one thing that is common with APIs is the need for a “date” field to specify a range of time to retrieve data for – and while AMPscript gives us some very intuitive date functions to work with – SSJS can be a little trickier to use. So lets go through a few basic principals that can help you to master the datetime data type in Javascript!

Step 1: Getting a Date value

There are many ways you can get and set a date value in Javascript, including setting the value manually, retrieving it from a Data Extension, or even rendering it in real time. Here are some examples:

var static_date = "January 1, 2021";

var de_date = Platform.Function.Lookup('CustomerData','DateofBirth',['FirstName','LastName'],['Angela','Ruiz'];

var Current_date = new Date();

Step 2: Convert String to a Date

Now we need to convert the datetime value into the Javascript “date” datatype. By converting your string or number value into the date datatype, Javascript will be able to use some of the specific date functions. You can read more about JS date functions here: https://www.w3schools.com/jsref/jsref_obj_date.asp

Lets use the date values we captured above. We can use the following JS codes to convert these dates into the correct JS date objects:

var static_date = new Date("January 1, 2021");

var de_date = Platform.Function.Lookup('CustomerData','DateofBirth',['FirstName','LastName'],['Angela','Ruiz'];
var new_de_date = new Date(de_date);

var Current_date = new Date();

Step 3: Altering a Date

Now that we have date objects, we can make adjustments to the date values easily using the JavaScript Date Functions. This could be needed for triggering a reminder communication a few hours before an appointment, or for setting a voucher expiry 30 days into the future. To achieve this, we can use the setHours() function to add or subtract hours from the date, or we could use the setDate() function to add or subtract days from the date. Here are some example of these functions in use:

static_date.setDate(static_date.getHours()-6); //subtracts 6 hours from the "static_date" value


new_de_date.setDate(new_de_date.getDate()+30); //adds 30 days to the "new_de_date" value

Current_date.setMonth(13); //adds 1 year to the "Current_date" value

Note that at the time of writing, Salesforce Marketing Cloud has a problem with the “setFullYear()” function, however the “setMonth(13)” workaround shown above is a suitable way to add years to your dates.

Step 4: Output your Date value

Now it’s time to output the date value into your code. I’ve seen a number of different date format requirements in APIs, so lets step through a little trick I use to make date formats as easy as possible.

Lets assume we need to produce the following date structure for our API call: 2021-05-14 00:00:00
The current date that we have in our date value is: Fri, 14 May 2021 20:58:13 GMT-06:00
So we can manually output the date parts in the correct format by using the following codes:

var NowDate = new Date("Fri, 14 May 2021 20:58:13 GMT-06:00");
var output= NowDate.getFullYear()+'-'+("0"+(NowDate.getMonth()+1)).slice(-2)+'-'+NowDate.getDate()+" 00:00:00";
//Output: 2021-05-14 00:00:00

What the code is doing:
Firstly we are setting the “NowDate” value using new Date();
Next we are building the “output” value by concatenating each of the date parts as we need them:
getFullYear() will return the YYYY date part.
getMonth() will return the month number from 0 to 11, so we need to +1. Some APIs enforce a 2-digit month value, so we can add a “0” to the front and use the slice() function to return only the last 2 characters in the string.
The getDate() function will give us the date from 1 to 31.
And lastly the ” 00:00:00″ is added to be compliant with the example date time format, assuming we wish to get data starting from midnight. The getHours(), getMinutes(), and getSeconds() functions could have been used to specify these dynamically if required.

Summary

I’ve seen some creative examples of people using AMPscript to set and alter the date values before using them in SSJS – however working with Dates in SSJS doesn’t need to be a burden. Below is a copy of my code snippet that I use for quickly building dates for API calls:

//Format: YYYY-MM-DD 00:00:00
var Today = new Date(); 
var Yesterday = new Date();
Yesterday.setDate(Yesterday.getDate()-1);

var Today_string = Today.getFullYear()+'-'+("0"+(Today.getMonth()+1)).slice(-2)+'-'+Today.getDate()+" 00:00:00";
var Yesterday_string = Yesterday.getFullYear()+'-'+("0"+(Yesterday.getMonth()+1)).slice(-2)+'-'+Yesterday.getDate()+" 00:00:00";
Read more

From my very first days in Salesforce Marketing Cloud learning how to create personalised content, I was always told that AMPscript is far easier to learn and much raster to run. While most typical digital marketers will agree with the first point, I’ve always held a shred of scepticism for the latter; is SSJS really slower than AMPscript?

I’ve been spending more time using SSJS recently, so I thought it was high time to qualify this long standing bias.

Methodology

To make these tests as fair as possible, I’ll be running each language in a cloud page with timestamp markers at the top and bottom of the code block being tested. The AMPscript and SSJS test functions will be as close as possible to one another, and I will run each code multiple times to achieve an average.
The following code was added to the top & bottom of each test to track the run times:

<script runat="server">
Platform.Load("Core","1");
var startDate = new Date();
</script>

//Code being tested

<script runat="server">
var endDate   = new Date();
var seconds = (endDate.getTime() - startDate.getTime()) / 1000;
Write("<br><br>Call took "+seconds+" seconds.");
</script>

Test 1 – A simple For Loop with no outputs

My first test was a simple FOR LOOP, testing how quickly the AMPscript and SSJS can execute a cruel 1 million loops.
The codes used for each language were as follows:

%%[
FOR @i=1 To 1000000 DO
NEXT
]%%
<script runat="server">
for (i = 0; i < 1000000; i++) {
}
</script>

AMPScript was able to complete this task in an average 7.5 seconds, while SSJS ran in an average 2.5 seconds!

I upped the ante to a near sisyphean task of 5 million loops (sorry Salesforce) to create more difference between the times.
AMPscript again came in 2nd place with an average 33 seconds, and SSJS completed in a respectable 12 seconds.

This was not what common knowledge was predicting would happen, so I altered the test conditions to see if I could find a weak point.

Test 2 – A Simple For Loop with some personalised text outputs

Similar to the first test, however this time there would be outputs. The “name” variable would be set on every loop to simulate personalisation. I also lowered the loop count to 10,000 to make sure the page load time could handle all the data being transferred.
The codes I tested were as follows:

%%[
FOR @i=1 To 10000 DO
SET @name = "Test"
output(concat("Hi there ",@name, " ", @i, "<br>"))
NEXT
]%%
<script runat="server">
for (i = 0; i < 10000; i++) {
var name = "Test"
  Write("Hi there " + name + " " + i + "<br>");
}
</script>

The AMPscript code ran in under 0.01 seconds every time, while the SSJS code averaged 4.5 seconds! To confirm the findings, I whispered a small prayer and ran it a few times at 100,000 loops.

AMPscript had processed all 100k rows in 0.75 seconds, while SSJS took on average 37 seconds to complete it’s 2.2 MB payload.

This was more inline with my expectations; however given how well SSJS performed in the non-output tests, LOOPS weren’t the problem, and there was clearly something more worth testing about how each language handled outputting data.

Test 3 – DE Lookups

A more practical and real world example is looking up and presenting personalised data from data extensions during an email send or cloud page load. To simulate this test I downloaded a list of over 900 Lord of the Rings characters (link) and loaded it into a DE. The code below looks up that data and then produces a table with all of the character info, which was looped over 10 times – producing over 9000 rows – to help emphasise a winner.

<table border="1">
<tr><th>Row</th><th>name</th><th>birth</th><th>death</th><th>gender</th><th>race</th></tr>  
%%[
FOR @x=1 TO 10 DO
SET @lotr = LookupRows('LOTR Characters','Show',1)
FOR @i=1 TO RowCount(@lotr) DO]%%
<tr><td>%%=v(FIELD(ROW(@lotr,@i),'Row'))=%%</td><td>%%=v(FIELD(ROW(@lotr,@i),'name'))=%%</td><td>%%=v(FIELD(ROW(@lotr,@i),'birth'))=%%</td><td>%%=v(FIELD(ROW(@lotr,@i),'death'))=%%</td><td>%%=v(FIELD(ROW(@lotr,@i),'gender'))=%%</td><td>%%=v(FIELD(ROW(@lotr,@i),'race'))=%%</td></tr>
%%[NEXT
NEXT]%%
</table>
<table border="1">
<tr><th>Row</th><th>name</th><th>birth</th><th>death</th><th>gender</th><th>race</th></tr>  
<script runat="server">
  for (x = 0; x < 10; x++) {
var lotr = DataExtension.Init("battleoftwolanguages");
var data = lotr.Rows.Lookup(["Show"], [1]);
  for (i = 0; i < data.length; i++) {
  Write("<tr><td>"+data[i]['Row']+"</td><td>"+data[i]['name']+"</td><td>"+data[i]['birth']+"</td><td>"+data[i]['death']+"</td><td>"+data[i]['gender']+"</td><td>"+data[i]['race']+"</td></tr>");
  }
}
</script>
</table>

AMPscript consistently ran 10 loops in under 0.5 second; while SSJS took 9 seconds to complete 10 loops. Not forgetting how well SSJS ran in the first test, I removed the text outputs (leaving only the lookup functions), and increased the loops to 100. This would remove the now known SSJS Achilles heal of text outputs and just test the lookup function.

After rerunning each language a few times, AMPscript was the clear winner at around 0.25 second for 100 loops, while SSJS really lagged behind on 25 seconds.

This confirmed that raw looping functions seemed to be SSJS’s strength, while it really struggled with any data or text handling. However there was still 1 practical use case left to test.

Test 4 – API GET Request

I decided to host a small .txt file on my web-server and use the AMPscript and SSJS HTTPGET requests to see how well each of them handles getting data from an external source. The text file only contained the word “empty” to keep data transfer size low, and I added the LOOP count to the call to ensure each request was unique (to bypass any caching).

I placed the call in a FOR LOOP for 50 cycles, as below:

%%[
FOR @x=1 TO 50 DO
set @HTMLContent = HTTPGet(concat(".../text.txt?i=",@i),false,0,@CallStatus)
output(concat(@HTMLContent,"<br>"))
NEXT
]%%
<script runat="server">
for (x = 0; x < 50; x++) {
var response = HTTP.Get(".../text.txt?x="+x);
  Write(response.Content + '<br />');
}
</script>

AMPscript continued it’s winning streak with a consistent run time of under 1 second, while SSJS took an average 20 seconds to complete all 50 calls.

I removed the output/write lines to see if that was affecting the times (like it did in previous tests), however after a few runs the execution times remained largely the same.

Summary

I think most SFMC Trailblazers would agree this was an unsurprising result, however it also wasn’t as bad as I thought it was going to be. Given most of these tests were run using FOR LOOPS that far exceed any practical use case, the run time difference for small batch processes would be practically negligible. Enterprise customers sending emails to millions of customers would experience a send time difference using SSJS for personalisation – however that kind of scale attracts other operational limitations/bottlenecks anyway.

For me the key learning has been that AMPscript is a faster language for Marketing Cloud to process, so use it over SSJS where possible; however don’t avoid SSJS for fears of degraded performance.
SSJS has some amazing benefits and strengths over AMPScript – such as JSON handling, WSProxy and GET Requests with headers – and it’s worth your time to understand the benefits and applications of each language.

Read more

Loops are one of my favorite processes in programming – they allow you to step through a variable amount of data/inputs to produce a customized output! Recently while discussing an API solution with a colleague I realized just how difficult it can be to explain how to use a loop function to solve a problem, and the differences between the various loop statements.

In an attempt to help other users to navigate this topic, lets cover the key recursive functions that are supported in Salesforce SSJS.

Introduction to the FOR Loop

In my opinion the FOR Loop is best SSJS loop statement to start with – it’s the most common, practical and understandable – and shares a very similar syntax with the AMPScript FOR statement, among other programming languages. Consider the following statement and the output below:

<script runat="server">
Platform.Load("core","1");

for (i = 0; i < 10; i++) {
  Write('The Count is: '+i+'<br>');
}

</script>

//Output:
The Count is: 0
The Count is: 1
The Count is: 2
The Count is: 3
The Count is: 4
The Count is: 5
The Count is: 6
The Count is: 7
The Count is: 8
The Count is: 9

The FOR statement contains 3 parts: The Initialization, Condition, and Ending Expression.

The Initialization runs a the start of the code and sets the scene. In the example above, it’s declaring that the variable “i” shall be equal to be 0.

After Initialization, the statement evaluates the expression in the Condition section. In the example above the Condition expression is “i < 10”. Is the value of “i” less than 10?
As the function just initialized and we just declared that i = 0, the expression evaluates as TRUE, and the area of code between the curly brackets “{}” is run.

After the code between the curly brackets has run, the third and last part of the statement is run, the Ending Expression. In the example above, the expression is “i++”, which is short hand for “i = i + 1” or “Add 1 to the value of i”. You can read more about JavaScript Operators here.
The value of “i” was previously 0, however this expression has incremented it by 1. Now that the Ending Expression is complete, the statement returns (loops back) to the Condition expression to be evaluated again.
“i < 10” is re-evaluated as TRUE – since “i” is now equal to 1, and “1 is less then 10” – and the code between the brackets is run again. Once complete, the statement processes the Ending Expression and increments the value of “i” by 1.

The process continues to “loop” over itself until the Condition expression evaluates as FALSE, at which time the statement concludes. In the example above, the last output was “The Count is 9” because as soon as the value of “i” reached 10, the Condition of “i < 10” was no longer true.

Practical uses for the FOR Loop

The beautiful simplicity of the FOR Loop is that it will simply run as many times as the statement requires it to. The example above used “(i = 0; i < 10; i++)” which resulted in the statement running 10 times. Having control over these values means we can make small adjustment to achieve other goals – such as counting down to 0, or counting in numbers other than 1.
Now that we understand the basics, lets use this functionality to solve a business problem.

Lets say we needed to send an email at the start of each day with a scoreboard that displays a list of employees who scored at least 100 points the previous day.
For simplicity lets also assume that the results from the previous day are saved directly into a Data Extension along with the employee names, so there is no need for any other lookups or date manipulation.
The data we have access to is as follows:

IDNamePoints
14Ameen245
6Coby45
42Nolan25
38Trey77
62Lee112
79Duke145
24Conrad37

Based on the above information, we can use the following SSJS code to print a table of the employees that met the condition:

<table border=1>
<tr><th>ID</th><th>Name</th><th>Points</th></tr>
  
<script runat="server">
Platform.Load("core","1");

var EMP_Points = DataExtension.Init("EmployeePointsCampaign");
var filter = {Property:"EMP_Points",SimpleOperator:"greaterThanOrEqual",Value:100};
var data = EMP_Points.Rows.Retrieve(filter);

for (i = 0; i < data.length; i++) {
  Write('<tr><td>'+data[i].EMP_ID+'</td><td>'+data[i].EMP_Name+'</td><td>'+data[i].EMP_Points+'</td></tr>');
}
</script>

</table>
Result:
IDNamePoints
14Ameen245
62Lee112
79Duke145

There’s a lot going on in the code above – so lets break it down!
Notice that the TABLE tags are outside of the script – this is because the LOOP statement is looping over the rows (tr) inside the table for each eligible employee.
You can read more about DataExtension lookups in SSJS here, however the key things to know is that the variable of “data” now holds a JSON object which contains the payload of data returned by the retrieve function that we need to output. You can brush up on your JSON understanding with this JSON intro at W3Schools. The payload returned by the retrieve function is below:

[{"EMP_ID":14, "EMP_Name":"Ameen", "EMP_Points":245},
{"EMP_ID":62, "EMP_Name":"Lee", "EMP_Points":112},
{"EMP_ID":79, "EMP_Name":"Duke", "EMP_Points":145}]

The object contains an array of objects equal to the number of eligible employees (3); where each object has within it an array of Name/Value pairs which hold the data (EMP_ID, EMP_Name, EMP_Points).
To access the name “Ameen”, we need to reference its address in the JSON structure. In this example, its “data[0]. EMP_Name”. The first address reference is data[0] because it’s in the first object in the JSON payload and arrays start at Ordinal “0”. Next we reference the Name that the value is stored in, which is “EMP_Name”.
To reference “Duke’s” point value of 145, we would write: “data[2]. EMP_Points” .

Now that we’ve covered how to address the data, lets explore how the Loop works in this example.
Notice how the Condition statement in this Loop is “i < data.length”. The “.length” property detects how many elements are in the JSON object – which in this example is 3 – therefore the Condition statement actually resolves as “i < 3”.
As we learnt in the earlier example, the for loop will continue until the “i” is no longer less than 3; meaning “i” will step through the values starting at 0 and ending at 2: [0,1,2].

Within the Loop statement, the output line is using the Write() function to print the table elements along with the 3 values that we need; EMP_ID, EMP_Name, EMP_Points. Each of these values is prefixed with “data[i]”, meaning that as the Loop statement steps through the values [0,1,2] it will correctly address each of the 3 objects in the data payload!

The FOR IN Loop

FOR IN is another form of JavaScript Loop that uses a slightly different condition format to determine how many times to run the statement. An example of the FOR IN Loop is as follows:

<script runat="server">
Platform.Load("core","1");
var EMP_data = {"EMP_ID":14, "EMP_Name":"Ameen", "EMP_Points":245};
for (i in EMP_data) {
  Write('The Value of ' +i+ ' is: ' +EMP_data[i]+ '<br>');
}
</script>

//Output:
The Value of EMP_ID is: 14
The Value of EMP_Name is: Ameen
The Value of EMP_Points is: 245

Remember above how we covered the Name/Value pairs in JSON objects? The FOR IN Loop allows you to select every “name” within an array and store it as a value; in the example above it’s stored as “i”.
With the Name attribute known, we can correctly address each Name/Value pair in the payload.

This Loop is very useful when you don’t know the name of every Name/Value pair and just need to cycle through everything that is returned. An example of this is where the Name attributes could be optional or change based on inputs. Rather than hard-coding them like we did in the first example, they can be dynamically referenced with FOR IN.

The DO WHILE Loop

The DO WHILE Loop is a slightly more complex version of the standard WHILE Loop. Since the WHILE Loop functions in much the same way as the FOR Loop , I’ll only cover the DO WHILE version.

This is the most complex Loop to learn, however the utility benefit of this loop can not be understated, it is a truly powerful function for any automation marketing team.
The DO WHILE statement comes in 2 parts – DO{} and WHILE().

<script runat="server">
Platform.Load("core","1");
var i = 0;
do {
  Write("Count to " + i + "<br>");
  i++;
}
while (i < 10);
</script>

//Output:
Count to 0
Count to 1
Count to 2
Count to 3
Count to 4
Count to 5
Count to 6
Count to 7
Count to 8
Count to 9

The above example is a replica of the FOR Loop we used at the top of this article, and it follows a very similar process of running a statement, the DO{} section, “while” a condition is TRUE, the WHILE() section.

This means we have direct control over when the statement ends based on values that can be evaluated as part of the statement. This can be used to interesting effect, such as running a random number generator that will output numbers until it hits the correct number:

<script runat="server">
Platform.Load("core","1");
var random = Math.floor(Math.random() * 10);
do {
  Write("random is " + random + "<br>");
  var random = Math.floor(Math.random() * 10);
}
while (random != 7);
</script>

//Output:
random is 4
random is 9
random is 8
random is 1
random is 3
random is 2
random is 8
random is 2
random is 9
random is 5
random is 9
random is 9
random is 8
random is 1
random is 4
random is 5
random is 0

In the above, the code will continue to run while the value of “random” is not equal to 7. In my test run it took 17 iterations before the number 7 was selected by the random number generator.

Now lets have some fun and make a function that needs to count up to a target number by adding randomly generated numbers together. It will keep generating random numbers (between 1-10) until it reaches its goal; disregarding numbers that will put it over the specified target of 100.

<script runat="server">
Platform.Load("core","1");
var target = 100;
var keepgoing = true;
var total = 0
var steps = 0
do {
  var random = Math.floor(Math.random() * 10)+1;
  steps++;
  if ((total + random) == target) {
    Write("The final Number was " + random + ", bringing to total to "+target+" on step number "+steps+"<br>");
    var keepgoing = false;
  } else {
   if ((total + random) > target) {
    Write("Number " + random + " disregarded - it would push the total to "+(total+random)+"<br>");
     var keepgoing = true;
   } else {
    Write("Number " + random + " added - total is "+(total+random)+"<br>");
    var total = (total + random);
     var keepgoing = true;
   }
  }
}
while (keepgoing);
</script>

//Output:
Number 2 added - total is 2
Number 4 added - total is 6
Number 8 added - total is 14
Number 8 added - total is 22
Number 6 added - total is 28
Number 6 added - total is 34
Number 3 added - total is 37
Number 6 added - total is 43
Number 8 added - total is 51
Number 1 added - total is 52
Number 2 added - total is 54
Number 2 added - total is 56
Number 8 added - total is 64
Number 7 added - total is 71
Number 7 added - total is 78
Number 7 added - total is 85
Number 5 added - total is 90
Number 6 added - total is 96
Number 9 disregarded - it would push the total to 105
Number 3 added - total is 99
Number 5 disregarded - it would push the total to 104
Number 2 disregarded - it would push the total to 101
Number 7 disregarded - it would push the total to 106
Number 9 disregarded - it would push the total to 108
Number 4 disregarded - it would push the total to 103
Number 7 disregarded - it would push the total to 106
Number 2 disregarded - it would push the total to 101
Number 8 disregarded - it would push the total to 107
Number 8 disregarded - it would push the total to 107
Number 2 disregarded - it would push the total to 101
Number 2 disregarded - it would push the total to 101
Number 2 disregarded - it would push the total to 101
Number 2 disregarded - it would push the total to 101
Number 7 disregarded - it would push the total to 106
Number 9 disregarded - it would push the total to 108
Number 7 disregarded - it would push the total to 106
Number 6 disregarded - it would push the total to 105
Number 4 disregarded - it would push the total to 103
Number 6 disregarded - it would push the total to 105
The final Number was 1, bringing to total to 100 on step number 40

Practical Examples for the DO WHILE Loop

The function above was creative, but the real world applications for this kind of loop are endless!
We could write an automated email that selects items from a customer’s cart in price acceding order until it reaches the minimum spend needed for Free Shipping. Imagine getting an email from your favorite fashion retailer with the subject line “Buy these 7 items in your wishlist today and get FREE SHIPPING!”
Retailers with annual spend milestones required to maintain a loyalty status – such as Sephora, Witchery and Mimco – could use the same logic to create a personalized Call to Action for the customer to maintain membership for another year. “Hey %%fName%%, restock on these %%count%% items and maintain %%tier%% status for another year!”

The versatility of SSJS Loops is that they can be run on Cloud Pages, in Emails, and as activities in Automation Studio, so there are plenty of ways to ingest and activate your data! Be sure to ask for help on the Salesforce StackExchange if you need assistance building your SSJS Loops.

Read more

I recently read the Marketing Cloud Security module on Trailhead and found the Secure Your Web and Landing Pages unit to be extremely interesting, however it glossed over the reason and function of the Example Headers that are recommended.

As the defined role gap between IT and Marketing is closing quickly, I thought this content needs to be given the attention it deserves from a non-IT perspective.

Website Headers and why you should care

Response headers are used to give a more detailed context of the response provided by a server. To critically over simplify this: When you hit SEARCH on Google.com your request is sent to Google to process – the response is then sent back and interpreted by your browser where is it rendered accordingly. If you are using Chrome, you can open the DevTools to see this in action: Instructions here.

With the example above, the response from Google contains a number of headers that instruct your browser what to do, for example:

content-encoding: br
content-type: text/html; charset=UTF-8
strict-transport-security: max-age=31536000
  • content-encoding: The response is compressed, and your browser needs to know which encodings were used by the server so it can decode the message.
  • content-type: The type of content contained in the response so that your browser knows how to render it.
  • strict-transport-security: This forces the browser to use the more secure HTTPS for communicating with the server, and sets a duration (in seconds) for this to be upheld.

So Headers are important. Why does Salesforce recommend we use these 6 on our pages?

The 6 headers outlined by Salesforce in their Landing Pages Security Best Practices provide an additional layer of security to prevent malicious actors from accessing your Cloud Page in ways that may hurt your business. Each of these 6 headers provide explicit instructions about how your Cloud Page can be accessed. Lets cover these in detail:

<script runat=server>
   Platform.Response.SetResponseHeader("Strict-Transport-Security","max-age=200");
   Platform.Response.SetResponseHeader("X-XSS-Protection","1; mode=block");
   Platform.Response.SetResponseHeader("X-Frame-Options","Deny");
   Platform.Response.SetResponseHeader("X-Content-Type-Options","nosniff");
   Platform.Response.SetResponseHeader("Referrer-Policy","strict-origin-when-cross-origin");
   Platform.Response.SetResponseHeader("Content-Security-Policy","default-src 'self'");
</script>

Strict-Transport-Security:
As above, this Header ensure all communication from a browser is sent over HTTPS. Without going into detail, HTTPS will protect your user’s data as it is sent to and from your Cloud Page. You really want to be using HTTPS if you are asking your subscribers to enter any form of personal information on your Cloud Page.
Note: SSL certificates are required to be installed by Salesforce Support and at the time of writing have an annual fee of ~$450 USD each. Adding this header without have an SSL installed on the appropriate Marketing Cloud Subdomain will result in the page request being blocked.

X-XSS-Protection:
XSS stands for Cross Site Scripting, and is a form of attach where a malicious actor injects scripts onto your page to make it perform differently.
This is a common header and most browsers support this by default. The “1; mode=block” value indicates that the header is active, and to block the page from rendering if a XSS attack is detected.

X-Frame-Options:
This header prevents your page from being loaded into a frame/iframe on another website.
The “Deny” value prevents any site from referencing you page in an iframe, however you can also use the “SameOrigin” or “Allow-From” values to specify trusted exceptions. You should always use this header unless you have an explicit plan for the page to be used in an iframe.

X-Content-Type-Options:
Content-Type sniffing is where an attacker can exploit a security vulnerability to interpret metadata from your site and possibly execute functions that the site was not built to expect. Forcing such errors can expose sensitive code or customer data.
Using the “nosniff” parameter tells the browser that the content-type is defined and not to be queried further.

Referrer-Policy:
The Referrer Policy controls how much referrer (origin site) information is sent in the request (destination site).
The “strict-origin-when-cross-origin” parameter will enforce HTTPS policy and only send the Full Referrer URL when the request comes from the same Origin. Cross-Origin requests will only see the base URL.

Content-Security-Policy:
This header acts as an additional layer of protection against Cross Site Scripting and other attacks.
The value recommended by Salesforce for this header is “default-src ‘self'”. This will force the browser to only load content that is served from the same place as the page with this header. This may cause problems with externally references images and CDN hosted JS libraries such as jQuery, so check the assets used on your page before enabling this one.

More ways to keep you Pages & Data safe

Cloud Pages are extremely versatile and powerful feature of Salesforce Marketing Cloud – however if used incorrectly can they can open up numerous serious security risks to your business and customers. Check with your company’s WebAdmin before enabling these headers and ask the Marketing Cloud community on Stack Exchange for help if you run into problems.

Read more

Working in Marketing Automation, I spend a lot of my time in Salesforce Marketing Cloud creating API integrations to push and pull data from various sources.

Writing these calls from scratch or copying bespoke code from previous solutions can be messy, so to save time I created a boilerplate of the basic OAuth 2.0 GetToken request process in AMPScript and SSJS that was easily editable for my purposes.

TL;DR. Here’s the codes!

AMPScript OAuth 2.0 using the V2 Token Endpoint:

%%[
VAR @httppost,@authurl,@apiid,@apistatusCode,@apiresponse,@apitoken
SET @apiid = '{"client_id": "zzzzzzzzzzzzzzzzzz","client_secret": "xxxxxxxxxxxxxxxxxxxxxxx", "grant_type": "client_credentials"}'
SET @authurl = "https://ccccccccccccccccccccc.auth.marketingcloudapis.com/v2/token";
SET @httppost = HTTPPost2(@authurl,"application/json",@apiid,false,@apistatusCode,@apiresponse)
SET @access_token = REGEXMATCH(@apistatusCode,'^(?:.*"access_token":")(.*?)(?:".*)$',1)
SET @rest_instance_url = REGEXMATCH(@apistatusCode,'^(?:.*"rest_instance_url":")(.*?)(?:".*)$',1)
]%%

access_token: %%=v(@access_token)=%% <br>
rest_instance_url: %%=v(@rest_instance_url)=%% <br>
apistatusCode: %%=v(@apistatusCode)=%% <br>

The Access Token can be retrieved using @access_token. Since the API response is in JSON – a data structure not supported by AMPScript – we need to use other string functions to extract the value we need, but more on that later.

SSJS OAuth 2.0 using the V2 Token Endpoint:

<script runat="server">
    Platform.Load("Core","1");
    var payload = {
    "grant_type": "client_credentials",
    "client_id": "yyyyyyyyyyyyyyyyyyy",
    "client_secret": "xxxxxxxxxxxxxxxxxx"
};
var authurl = "https://zzzzzzzzzzzzzzzzzz.auth.marketingcloudapis.com/v2/token";
var result = HTTP.Post(authurl, 'application/json', Stringify(payload));
  
if (result.StatusCode == 200) {
    var responseJson = Platform.Function.ParseJSON(result.Response[0]);
    var accessToken = responseJson.access_token;
    var restUrl = responseJson.rest_instance_url;
} else {
    throw new Error("Error fetching access token");
}
Write(result.Response[0]); //payload response
Write(accessToken); //token
</script>

Luckily Server Side JavaScript is able to parse JavaScript Object Notation (JSON), so the “access_token” value can be referenced and stored in the variable “accessToken” for later use.

Parsing JSON Values in Salesforce Marketing Cloud

If you work with API endpoints you’ll be familiar with the XML and JSON content types. JSON took the most-popular content-type throne from XML in around 2013 and has been the dominant format ever since. As of February 2020 there is still no AMPScript-only solution for parsing JSON values in Salesforce Marketing Cloud, so we need to employ other tools to extract the values we need.

Below is an Example JSON Response of the V2 Token Endpoint from earlier:

{
"access_token":"eyJhbLciOiJIPzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjIifQ.eyJhY2Nlc3NfdG9rZW4iOiJhYmJUQTlpSHZqRjkyd3Jkb0xWZEFCaloiLCJjbGllbnRfaWQiOiI3ZTRmYW1xaWUzcWtzdzlhNDRrcmxvZDgiLCJlaWQiOjEwNzU3Njc2LCJzdGFja19rZXkiOiJRQTFTMSJ9.wSFfEdeNrkoiU_tnmJ2ihm8iUqnJKlZoI3GlavTGBhs.hU4EsiC1e9txh_TCt90YlI2l7xZZ5E6_oa0xku3Jj9CCk1B72M4bhO3kUIyhwfVuB0MFbL0y9KD_RRFzg-nuqPgjPyONnby-iWopdZPBHd-3woupxCMST5-vfJO9qAED9qiUfYLS4WmHRuJTCX4NPScyu8BdROTVEe-D3iAoAeFoJX_rLZ9d5eEhIn1AvkYgoj9siuxAprHEvmySTgNIXkQA6uT_IQ-H1dbfOyJmlFKpYzvhvHb0KH7NJ24zy5bd2MQ5",
"expires_in":1200,
"token_type":"Bearer",
"rest_instance_url":"mc563885gzs27c5t9-63k636tzgm.rest.marketingcloudapis.com",
"soap_instance_url":"mc563885gzs27c5t9-63k636tzgm.soap.marketingcloudapis.com",
"scope": "email_read email_write email_send"
}

In the example above, we could use a SubString function to extract the specific character index range that we need for the token. In this example, it’s character 17 through 529; or from character 17 for a length of 512 characters, written as: %%=Substring(@response,17,512)=%% in AMPScript.

SubString works well so long as the response payload doesn’t change in any way, however by default JSON is an unordered set of name/value pairs, so even though Salesforce does preserve object orders, we shouldn’t rely on it.

RegEx is the most reliable way to extract a string value from a JSON payload in AMPScript. I cover this topic in more detail here: RegEx to get JSON values in AMPScript

Read more

Server Side JavaScript is a powerful language in Salesforce Marketing Cloud with numerous practical applications for digital marketers. It can lookup Data Extensions, it has a simple WSProxy model for accessing SOAP objects, and best of all – it can handle JSON objects!

However developing these kinds of activities in SFMC can sometimes be tedious due to the lack of error detection and handling in Emails and Cloud Pages. Before using this function I could easily waste hours hunting down my SSJS code errors!

So here is a SSJS code snippet that I use to detect and handle any errors in my Cloud Pages:

<script runat="server">
Platform.Load("Core","1");
try{
//Do your SSJS Functions Here
} 
catch(error) {
Write('Message: '+ error);
}
</script>

The try statement identifies a block of code to watch for errors while it’s being run.
The catch(error){} statement works like an if statement – it only runs if the condition “was there an error in the try section” is true. The variable “error” is where the the exception message – why the error occurred – will be stored.
In the code above, I’ve used the “error” variable inside of a Write() function so that my Cloud Page outputs the cause of any errors that occur.

Why should I use Try-Throw-Catch

Everyone makes mistakes when writing code, and the worst kind of mistakes are the ones you can’t find! Normally I like to us the Output/Print/Write functions when developing to see where the error occurred in my code – however this isn’t possible in SFMC, as the Cloud Page just fails to load or throws an ambiguous 500 error.

Using the Try-Throw-Catch function above will ensure you get a response from the page, and if an error has occurred, you’ll have some clues about what went wrong.

What about the “Throw” part?

By default, the try statement only listens for invalid operations – like syntax errors. However you can create your own “errors” by using the throw statement. For example:

<script runat="server">
Platform.Load("Core","1");
try{
//Do your SSJS Functions Here
var subKey = Attribute.GetValue("_subscriberkey");
if (!subKey) {throw "Empty SubscriberKey";}
} 
catch(error) {
Write('Message: '+ error);
}
</script>

In the code above, I’m trying to set the Cloud Page attribute “_subscriberkey” into the variable “subKey”.

If this page was accessed via a %%=RedirectTo(CloudPagesURL(x))=%% link in an email, the recipient’s SubscriberKey attribute would be populated as part of the Query String, and page would load.
However if the page was access directly and without a QS parameter, then the _subscriberkey attribute will be empty, and the page will throw the custom error I created.

Further Reading & Resources

I highly recommend reading through some of the JS Documentation on error handling; it’s a great way to speed up your development and put controlled exceptions around unstable 3rd party API calls.
Here’s a few resources I used to learn how the try statement works:

Read more