25 Salesforce Marketing Cloud SSJS examples
If you have ever worked with Server-Side JavaScript (SSJS) in Salesforce Marketing Cloud, you already know two things, it’s incredibly powerful and at the same time full of limitations. I have curated 50 SSJS scripts that I have used at least once in a production environment. There is no specific order – I wrote them as they came to mind. The best way to read this article is to check the table of contents and find any example that interests you.
Read a query string parameters safely
When working with SSJS in Salesforce Marketing Cloud, you’ll often reach for:
Platform.Request.GetQueryStringParameter('id');
It works – but it’s not always safe or reliable in real-world scenarios, so let’s overengineer it a bit and wrap it in a function.
<script runat="server">
Platform.Load("Core","1");
// Function to safely get query string parameter
function getQueryParam(paramName, defaultValue) {
try {
var value = Platform.Request.GetQueryStringParameter(paramName);
// Normalize
if (value === null || value === undefined || value === '') {
return defaultValue || '';
}
return String(value);
} catch (e) {
return 'Error: ' + String(e);
}
}
try {
var token = getQueryParam('token', ''),
authorized = token === 'my-secret'
if (!authorized) throw "Nothing to see here."
//show secret content
} catch (e) {
Platform.Response.Write('Error: ' + String(e));
}
</script>
This is the simplest form of protection for CloudPages. Without it, anyone can call your page and trigger logic (data lookups, inserts, API calls). Even a basic shared secret drastically reduces abuse.
Process form submission in cloud page
I have a confession – I’ve never used CloudPages Smart Capture. Since I wanted to be a developer, I skipped it entirely and went straight to AMPscript and SSJS for form submissions.
<!DOCTYPE html>
<html>
<head>
<title>Signup Form</title>
</head>
<body>
<script runat="server">
Platform.Load("Core", "1.1.1");
var request = Platform.Request;
var errors = [];
// Email validation function
function isValidEmail(email) {
var regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!regex.test(email)) return false;
email = email.toLowerCase().trim();
var blockedDomains = [
"test.com",
"example.com",
"mailinator.com",
"tempmail.com"
];
var domain = email.split("@")[1];
if (blockedDomains.indexOf(domain) > -1) return false;
if (email.indexOf("asdf") > -1) return false;
if (email.indexOf("test") === 0) return false;
return true;
}
if (request.Method === "POST") {
try {
var email = request.GetFormField("email");
var firstname = request.GetFormField("firstname");
var country = request.GetFormField("country");
// Validation
if (!email) {
errors.push("Email is required");
} else if (!isValidEmail(email)) {
errors.push("Invalid email address");
}
if (!country) {
errors.push("Country is required");
}
// If no errors → insert + redirect
if (errors.length === 0) {
var result = Platform.Function.InsertData(
"Form_Submissions",
["EmailAddress", "FirstName", "Country", "CreatedDate"],
[email, firstname, country, Now()]
);
// Redirect to thank you page
Platform.Response.Redirect(CloudPagesURL(3333));
}
} catch (e) {
errors.push("Unexpected error occurred");
//you can also log errors into data extension using logger function you can find it later in this article
// and redirect to thank you page so the user experience is intact.
}
}
if (errors.length > 0) {
Write("<div style='color:red;'>");
for (var i = 0; i < errors.length; i++) {
Write("• " + errors[i] + "<br>");
}
Write("</div><br>");
}
</script>
<form method="POST" action="%%=RequestParameter('PAGEURL')=%%">
<input type="hidden" name="submitted" value="true">
<label>Email:</label><br>
<input type="email" name="email"><br><br>
<label>First Name:</label><br>
<input type="text" name="firstname"><br><br>
<label>Country:</label><br>
<input type="text" name="country"><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
Retrieve Data Extension Rows
We have more than a handful of different ways to retrieve data from Data Extensions using SSJS. We will start with some very simple examples without filters and gradually move to more advanced ones that use complex filters and loops. The following lookup methods have a limitation of retrieving only the first 2,500 rows after filtering.
Rows.Retrieve
<script runat="server">
var de = DataExtension.Init("Master_DE");
var rows = de.Rows.Retrieve();
</script>
Rows.Lookup
<script runat="server">
var de = DataExtension.Init("Master_DE");
var rows = de.Rows.Lookup(["name","last_name"], ["John","Doe"],10,"name");
</script>
There are also Platform functions like Lookup, LookupRows, and others that you may have seen used in AMPscript.
Retrieve Data Extension Rows With Simple Filter
It’s really good practice to keep the Name and External Key the same when creating a Data Extension. This helps ensure you always use the correct identifier, since some functions use the Name while others rely on the External Key for data retrieval.
In the example below, DataExtension.Init uses the External Key.
<script runat="server">
var de = DataExtension.Init("Master_DE");
var rows = de.Rows.Retrieve({
Property: "Email",
SimpleOperator: "equals",
Value: email
});
</script>
Supported operators:
- equals
- notEquals
- greaterThan
- lessThan
- like
- isNull
- isNotNull
Retrieve Data Extension Rows With Advanced Filter
Usually, when we select anything from the database, we use filters. SSJS comes with various filtering methods, but it is not as advanced as having SQL at hand. The best practice is to complement SSJS with pre-made queries that run within automation.
<script runat="server">
var de = DataExtension.Init("Master_DE");
var rows = de.Rows.Retrieve({
LeftOperand: {
Property: "Country",
SimpleOperator: "equals",
Value: "DE"
},
LogicalOperator: "AND",
RightOperand: {
Property: "Status",
SimpleOperator: "equals",
Value: "Active"
}
});
</script>
You can add even nested conditions but for some object not all operators are supported the best thing to do before you want to create any complex filter is to check object definition and if there are any limitations. For example automation object can only use in and equals. So any date filters wont work as expected
<script runat="server">
var de = DataExtension.Init("Master_DE");
var filter = {
LeftOperand: {
LeftOperand: {
Property: "Country",
SimpleOperator: "equals",
Value: "DE"
},
LogicalOperator: "OR",
RightOperand: {
Property: "Country",
SimpleOperator: "equals",
Value: "AT"
}
},
LogicalOperator: "AND",
RightOperand: {
Property: "Status",
SimpleOperator: "equals",
Value: "Active"
}
};
var rows = de.Rows.Retrieve(filter);
</script>
Retrieve Data Extension Rows Using WSProxy
<script runat="server">
var api= new Script.Util.WSProxy();
var cols = ["Email", "FirstName"];
var res = api.retrieve(
"DataExtensionObject[Master_DE]",
cols
);
var rows = res.Results;
</script>
Advanced data retrieves with WSProxy
We can retrieve not only Data Extensions, but also many other SOAP service objects. In addition to retrieving data, we can also modify, add, or remove records, essentially performing full CRUD operations on these objects.
<script runat="server">
Platform.Load("core", "1");
var prox = new Script.Util.WSProxy();
// Object type for Automation Studio
var objectType = "Automation";
// Fields to retrieve
var cols = [
"Name",
"CustomerKey",
"Status",
"CreatedDate",
"LastRunTime",
"LastSaveDate"
];
// Optional filter (only active automations)
var filter = {
Property: "Status",
SimpleOperator: "equals",
Value: 2 // 2 = Active
};
var moreData = true;
var reqID = null;
var total = 0;
while (moreData) {
moreData = false;
var data = (reqID == null)
? prox.retrieve(objectType, cols, filter)
: prox.getNextBatch(objectType, reqID);
if (data != null) {
moreData = data.HasMoreRows;
reqID = data.RequestID;
if (data.Results && data.Results.length > 0) {
for (var i = 0; i < data.Results.length; i++) {
var a = data.Results[i];
Write(
a.Name + " | Status: " + a.Status +
" | Last Run: " + a.LastRunTime + "<br>"
);
total++;
}
}
}
}
Write("<br>Total Automations: " + total);
</script>
Insert data into data extensions
Similar to the various retrieve methods, we also have a handful of ways to insert data into Data Extensions. Here, we will mostly use AMPscript functions wrapped in SSJS. One thing to keep in mind is that AMPscript can be more forgiving, but in SSJS you need to use the exact function names as defined – otherwise, you will encounter a server runtime error.
InsertData
SSJS function to insert data from CloudPages, landing pages, microsites, and SMS messages in MobileConnect.
<script runat="server">
Platform.Load("core", "1");
try {
var response = Platform.Function.InsertData(
"Order_Log",
["OrderID", "EmailAddress", "Amount", "OrderDate", "Status"],
["ORD-10001", "john.doe@email.com", 129.99, Now(), "Completed"]
);
if (response == 1) {
Write("Order record created");
} else {
Write("Insert failed");
}
} catch (e) {
Write("Error: " + Stringify(e));
}
</script>
InsertDE
Similar to its AMPscript sibling this function is best to use within email send.
<script runat="server">
Platform.Load("core", "1");
var response = Platform.Function.InsertDE(
"Order_Log",
["OrderID", "EmailAddress", "Amount", "OrderDate", "Status"],
["ORD-10001", "john.doe@email.com", 129.99, Now(), "Completed"]
);
</script>
UpsertData
To get a proper searchable list of file transfer locations first we need to perform REST API call to get json array of locations. Then we can parse JSON and save file locations into data extension.
<script runat="server">
try{
var fileTransferLocations =
[
...
{
"customerKey": "customer-key",
"name": "File location name",
"description": "",
"locationType": "ExternalSftp",
"sFtpFileTransferLocation": {
"portNumber": 22,
"userName": "groot",
"url": "https://martechnotes.com",
"authType": "Password"
}
}
],fields = [], values= [];
for(var i=0;i<fileTransferLocations.length;i++){
fields = [];
values = [];
fields.push('locationType');
values.push(fileTransferLocations[i].locationType);
if (fileTransferLocations[i].hasOwnProperty('sFtpFileTransferLocation')){
fields.push('url');
values.push(fileTransferLocations[i].sFtpFileTransferLocation.url);
fields.push('user');
values.push(fileTransferLocations[i].sFtpFileTransferLocation.userName);
}
Platform.Function.UpsertData( "file_locations_info",['name','externalKey'],[fileTransferLocations[i].name,fileTransferLocations[i].customerKey],fields, values);
}
}catch(e){
Platform.Response.Write(Platform.Function.Stringify(e));
}
</script>
UpsertDE
Similarly to insertDE this function is to be used in sendable context within email or sms templates
<script runat="server">
Platform.Load("core", "1");
var result = Platform.Function.UpsertDE(
"Customer_Profile",
["EmailAddress"], // Primary key
["john.doe@email.com"], // Lookup value
["FirstName", "LastName", "Status", "LastLoginDate"],
["John", "Doe", "Active", Now()]
);
if (result > 0) {
Write("Customer profile updated or inserted");
} else {
Write("No changes made");
}
</script>
UpdateData
<script runat="server">
Platform.Load("core", "1");
try {
var result = Platform.Function.UpdateData(
"Order_Status",
["OrderID"], // Lookup field
["ORD-20240301"], // Lookup value
["Status", "ShippedDate"], // Fields to update
["Shipped", Now()] // New values
);
if (result > 0) {
Write("Order status updated");
} else {
Write("Order not found");
}
} catch (e) {
Write("Error: " + Stringify(e));
}
</script>
UpdateDe
<script runat="server">
Platform.Load("core", "1");
try {
var result = Platform.Function.UpdateDE(
"Order_Status",
["OrderID"], // Lookup field
["ORD-20240301"], // Lookup value
["Status", "ShippedDate"], // Fields to update
["Shipped", Now()] // New values
);
if (result > 0) {
Write("Order status updated");
} else {
Write("Order not found");
}
} catch (e) {
Write("Error: " + Stringify(e));
}
</script>
Send API Requests
I will give here to most versatile HTTP request we have included in our SSJS within Salesforce Marketing Cloud.
<script runat="server">
/* Load core beacause we do not want to write Platform.Function everytime :) */
Platform.Load('Core','1');
/* Create an authentication string to pass as a request header */
var token = "exapmle_token";
var auth = "Bearer " + token;
/* Specify the request body as a string */
var requestBody = '{name: x,email:me@example.com}';
try {
/* Initialize the request handler */
var request = new Script.Util.HttpRequest("https://www.api.example.com/put");
/* Set request headers */
request.setHeader("Authentication", auth);
request.setHeader("sample-header", "HeaderValue");
/* Configure the request properties */
request.method = "PUT";
request.encoding = "UTF-8";
request.postData = requestBody;
request.contentType = "application/json";
/* Send the request */
var response = request.send();
/* Necessary lines to process the response */
var responseString = String(response.content);
var responseJSON = ParseJSON(responseString );
/* Output the response body */
Write(Stringify(responseJSON));
} catch(e) {
Write(Stringify(e));
}
</script>
Remove data from autosupressions or data extensions
Very simple, yet very powerful. One of the few ways to clean suppression lists in Salesforce Marketing Cloud is to use a simple SSJS snippet that does the job. This is not only a great helper for suppression lists, but also useful anytime you need to purge a Data Extension – for example, when running daily stats or other recurring activities where you need to start with a clean slate.
<script runat="server">
Platform.Load("Core","1");
var api = new Script.Util.WSProxy(),
supressionLists = [
// { CustomerKey: '2A7C2304-23A7-AEF0-C9BD-43DC21C860FE'},
// { CustomerKey: '2A7C2304-23A7-AEF0-C9BD-43DC21C860FE'},
],data;
for (var i=0;i<supressionLists.length;i++){
data = api.performItem("DataExtension", supressionLists[i], "ClearData", {});
Write(Platform.Function.Stringify(data));
}
</script>
Create data extensions using WSProxy
Creating Data Extensions via SSJS using WSProxy is useful when you want to standardize structures or automate setup instead of manually creating them in the UI. This approach is especially helpful when deploying consistent schemas across multiple environments or projects.
<script runat="server">
Platform.Load("core", "1");
var api = new Script.Util.WSProxy();
// Data Extension configuration
var config = {
CustomerKey: "example_de_key",
Name: "Example_Data_Extension",
CategoryID: 12345, // Folder ID
Fields: [
{ Name: "SubscriberKey", FieldType: "Text", MaxLength: 100, IsPrimaryKey: true, IsRequired: true },
{ Name: "EmailAddress", FieldType: "EmailAddress", MaxLength: 254, IsRequired: true },
{ Name: "FirstName", FieldType: "Text", MaxLength: 100, IsRequired: false },
{ Name: "LastName", FieldType: "Text", MaxLength: 100, IsRequired: false },
{ Name: "Country", FieldType: "Text", MaxLength: 2, IsRequired: true },
{ Name: "Language", FieldType: "Text", MaxLength: 5, IsRequired: true },
{ Name: "Status", FieldType: "Text", MaxLength: 50, IsRequired: true },
{ Name: "Score", FieldType: "Number", IsRequired: false },
{ Name: "IsActive", FieldType: "Boolean", IsRequired: false },
{ Name: "CreatedDate", FieldType: "Date", IsRequired: true }
],
// Sendable configuration
IsSendable: true,
SendableDataExtensionField: {
Name: "SubscriberKey",
FieldType: "Text"
},
SendableSubscriberField: {
Name: "Subscriber Key"
}
};
// Create Data Extension
try {
var result = api.createItem("DataExtension", config);
Write("Data Extension created successfully");
} catch (e) {
Write("Error creating Data Extension: " + Stringify(e));
}
</script>
Delete data extensions using WSProxy
Just like we can create Data Extensions, we can also act as executioners and remove them with no mercy using a script. Keep in mind that these will be deleted permanently – nothing will be moved to the recycle bin. You should only execute this when you are 100% sure about the outcome.
<script runat="server">
Platform.Load("core", "1.1.1");
var api = new Script.Util.WSProxy();
api.deleteItem("DataExtension", {
"CustomerKey": "your-customer-key"
});
</script>
<script runat="server">
Platform.Load("core", "1.1.1");
var deNames = [
'test_delete'
],
i = 0;
var retrieveDataExtension = function(name){
var api = new Script.Util.WSProxy();
var cols = [
"ObjectID",
"PartnerKey",
"CustomerKey",
"Name",
"CreatedDate",
"ModifiedDate",
"Client.ID",
"Description",
"IsSendable",
"IsTestable",
"SendableDataExtensionField.Name",
"SendableSubscriberField.Name",
"Template.CustomerKey",
"CategoryID",
"Status",
"IsPlatformObject",
"DataRetentionPeriodLength",
"DataRetentionPeriodUnitOfMeasure",
"RowBasedRetention",
"ResetRetentionPeriodOnImport",
"DeleteAtEndOfRetentionPeriod",
"RetainUntil",
"DataRetentionPeriod"
];
var request = api.retrieve("DataExtension", cols, {
Property: "Name",
SimpleOperator: "equals",
Value: name
});
if(request.Status == "OK"){
return request.Results.shift();
}else return null;
}
try{
var properties,
result,
api = new Script.Util.WSProxy();
for (i=0;i<deNames.length;i++){
properties = retrieveDataExtension(deNames[i])
result = api.deleteItem("DataExtension", {
"CustomerKey": properties.CustomerKey
});
if (result.Status == "Ok")
Write("Deleted- " + properties.Name + " by Customer Key " + properties.CustomerKey );
else
Write(Stringify(result))
}
}catch(e){
Write(Stringify(e))
}
</script>
Add columns to data extensions
This is very useful when adding fields to Data Extensions with many columns, as the UI tends to freeze and it takes ages. Not saying that having a large number of columns is the best approach, but we’ve all had projects where master tables span hundreds of columns. Also another example is to add one or more columns to multiple data extensions. Adding fields with SSJS takes effect immediately and avoids any UI freezing.
<script runat="server">
Platform.Load("Core","1.1.1");
var customerKeys = [
"FC2C3BCF-3740-4433-A103-E6A1F2F47120",
"A0164833-3747-4444-45DA-A01648347120"
];
// Define the new field once
var newField = {
Name : "new_field",
CustomerKey : GUID(),
FieldType : "Text",
MaxLength : 1,
IsRequired : false,
DefaultValue : "Y"
};
// Loop through the DEs and add the field
for (var i = 0; i < customerKeys.length; i++) {
try {
var de = DataExtension.Init(customerKeys[i]);
var result = de.Fields.Add(newField);
Write("Added field to DE: " + customerKeys[i] + "<br>");
} catch(e) {
Write("Error on DE: " + customerKeys[i] + " - " + Stringify(e) + "<br>");
}
}
</script>
Set Up Logging for CloudPages and Automations
Another really good real-life example is setting up proper logging. This is very useful when debugging your script in an automation, monitoring the outcome, or coming back to it when issues are raised and the issue lays withing your script activity.
<script runat="server">
Platform.Load("Core", "1.1.1");
var isCloudPage = false,
logDataExtension = "my_log_de";
function logMessage(type, message, htmlOutput) {
function logToDataExtension(type, message) {
Platform.Function.InsertDE(
logDataExtension,
["type", "message"],
[String(type || ""), String(message || "")]
);
}
if (!isCloudPage) {
logToDataExtension(type, message);
} else {
Write(htmlOutput ? htmlOutput : "<br>" + String(message || ""));
}
}
</script>
Get query definition activities
This is used plenty when any field or data extension is removed and we want to check where the data extension or field is used so we can update our query definitions and prevent from unwanted failures
<script runat="server">
Platform.Load("Core","1");
var prox = new Script.Util.WSProxy(),
objectType = "QueryDefinition",
cols = ["Name", "CustomerKey","CreatedDate","ObjectID","QueryText"], // Adjusted properties relevant to QueryDefinition
moreData = true,
reqID = null,
numItems = 0,
results=[];
while(moreData) {
moreData = false;
var data = reqID == null ?
prox.retrieve(objectType, cols) :
prox.getNextBatch(objectType, reqID);
if(data != null) {
moreData = data.HasMoreRows;
reqID = data.RequestID;
if(data && data.Results) {
for(var i=0; i < data.Results.length; i++) {
// Example of logging the query definition details
var result = data.Results[i];
Platform.Function.UpsertData(
"query_definition_info",
['Name','CustomerKey','ObjectID'],
[result.Name,result.CustomerKey,result.ObjectID],
['CreatedDate','QueryText',],
[result.CreatedDate,result.QueryText]
);
numItems++;
}
}
}
}
Platform.Response.Write("<br />" + numItems + " total " + objectType + " items found.");
</script>
Remove query definitions
Also there are times that entire set of query activities has to be removed and there is nothing such as builk select and remove in the ui and there fore here comes the power of scripting.
<script runat="server">
Platform.Load("Core", "1.1.1");
var api = new Script.Util.WSProxy(),
res,
batch = [],
toRemove =
[""]//array of customer keys to be removed
try{
for (var i = 0; i < toRemove.length; i++){
Write("Removing " + toRemove[i] + " ...... " );
res = QueryDefinition.Init(toRemove[i]).Remove();
Write(res);
Write("<br>");
}
}catch(e){
Write(Stringify(e.Description));
}
</script>
Change table occusrence in query definitions
You can change various settings of a Query Definition object, and one of them is the QueryText. You can either replace the entire query or simply search and replace specific parts as needed.
<script runat="server">
Platform.Load("Core", "1.1.1");
// Query Definition External Keys
var queryKeys = [
"customer-key-1",
"customer-key-2"
];
// Replace logic
var searchFor = /old_table/gi;
var replaceWith = "new_table";
for (var i = 0; i < queryKeys.length; i++) {
var key = queryKeys[i];
try {
// Init Query Definition directly
var qd = QueryDefinition.Init(key);
// Get current SQL
var originalQuery = String(qd.QueryText);
if (!originalQuery) {
Write("No query found for key: " + key + "<br>");
continue;
}
// Replace text
var updatedQuery = originalQuery.replace(searchFor, replaceWith);
if (originalQuery === updatedQuery) {
Write("No changes needed for: " + key + "<br>");
continue;
}
// Update Query
var result = qd.Update({
QueryText: updatedQuery
});
if (result === "OK") {
Write("Updated: " + key + "<br>");
} else {
Write("Failed: " + key + "<br>");
}
} catch (e) {
Write("Error for " + key + ": " + Stringify(e) + "<br>");
}
}
</script>
Advanced examples
For my fellow premium users I have also prepared set of additional scripts that are full
🔒 This content is for Premium Subsribers only.
Please log in to preview content. Log in or Register
You must log in and have a Premium Subscriber account to preview the content.
When upgrading, please use the same email address as your WordPress account so we can correctly link your Premium membership.
Please allow us a little time to process and upgrade your account after the purchase. If you need faster access or encounter any issues, feel free to contact us at info@martechnotes.com or through any other available channel.
To join the Discord community, please also provide your Discord username after subscribing, or reach out to us directly for access.
You can subscribe even before creating a WordPress account — your subscription will be linked to the email address used during checkout.
Premium Subscriber
19,99 € / Year
- Free e-book with all revisions - 101 Adobe Campaign Classic (SFMC 101 in progress)
- All Premium Subscriber Benefits - Exclusive blog content, weekly insights, and Discord community access
- Lock in Your Price for a Full Year - Avoid future price increases
- Limited Seats at This Price - Lock in early before it goes up









