SfSkills Domains Docs GitHub

Home / apex / ssjs-server-side-javascript

ssjs-server-side-javascript

Use this skill when writing, debugging, or reviewing Server-Side JavaScript (SSJS) in Salesforce Marketing Cloud — Script Activities, Cloud Pages, and Landing Pages.

Open the skill folder on GitHub


SSJS — Server-Side JavaScript

This skill activates when a practitioner needs to author, debug, or architect Server-Side JavaScript (SSJS) code running inside Salesforce Marketing Cloud. It provides grounded guidance on execution environments, WSProxy for SOAP access, HTTP functions for REST calls, error handling, variable scoping rules, and the SSJS/AMPscript interoperability model.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

Execution Environments and the <script runat="server"> Tag

SSJS code must be wrapped in <script runat="server"> tags. Without the runat="server" attribute the block is treated as client-side JavaScript and does not execute on the Marketing Cloud server. SSJS can coexist with AMPscript in the same file — the Marketing Cloud rendering engine processes AMPscript substitutions first, then evaluates SSJS blocks. This allows AMPscript variables to be referenced inside SSJS if the AMPscript block is declared before the SSJS block.

WSProxy — Preferred SOAP API Client

WSProxy (Script.Util.WSProxy) is the built-in Marketing Cloud SOAP API client for SSJS. It is the correct and preferred way to interact with Marketing Cloud objects (Data Extensions, Subscribers, Sends, etc.) from SSJS because:

The core pattern:

var prox = new Script.Util.WSProxy();
var cols = ["SubscriberKey", "EmailAddress", "Status"];
var filter = {
  Property: "Status",
  SimpleOperator: "equals",
  Value: "Active"
};
var result = prox.retrieve("Subscriber", cols, filter);

Retrieve results are paged. If result.HasMoreRows is true, use prox.getNextPage() to iterate.

Script.Util.HttpRequest — Outbound REST/HTTP

For calls to external REST APIs or non-Marketing Cloud SOAP endpoints, use Script.Util.HttpRequest. This is a synchronous HTTP client that supports GET, POST, PUT, PATCH, and DELETE. Set headers and body before calling request.send().

var req = new Script.Util.HttpRequest("https://api.example.com/data");
req.emptyContentHandling = 0;
req.retryCount = 2;
req.setHeader("Content-Type", "application/json");
req.setHeader("Authorization", "Bearer " + token);
req.method = "POST";
req.postData = Stringify(payload);
var resp = req.send();
var body = Platform.Function.ParseJSON(resp.content);

Error Handling and the Write() Logging Pattern

SSJS does not surface uncaught exceptions to the user in a useful way in Script Activities — an uncaught exception causes the entire Activity step to fail with a generic error, which makes diagnosis difficult. The mandatory pattern is try/catch around all significant operations with Write() logging inside the catch block.

Write() outputs text to the Script Activity log tab in Automation Studio, making it the primary debugging mechanism. Log the error message, stack if available, and any relevant variable state before re-throwing or gracefully continuing.

ES3 Dialect Constraints

The SSJS engine is ES3-compatible. This means:


Common Patterns

WSProxy Upsert to a Data Extension

When to use: Bulk insert or update records in a Data Extension from a Script Activity, typically after retrieving data from an external API or another system.

How it works:

<script runat="server">
Platform.Load("Core", "1.1.1");
try {
  var prox = new Script.Util.WSProxy();
  var rows = [
    { keys: { SubscriberKey: "abc123" }, values: { FirstName: "Ana", Score: "95" } },
    { keys: { SubscriberKey: "def456" }, values: { FirstName: "Ben", Score: "80" } }
  ];
  var result = prox.upsertBatch("DataExtensionObject", rows, { Name: "My_DE_ExternalKey" });
  Write("Upserted: " + result.Status);
} catch(e) {
  Write("ERROR: " + e.message);
}
</script>

Why not the alternative: Using AMPscript UpsertDE() works for small single-record updates inside sends, but it cannot handle batch operations, does not provide programmatic status checking, and is not appropriate for Automation Studio Script Activities.

Outbound REST Call with Error Logging

When to use: Pulling data from an external REST API (CRM, ERP, custom backend) inside a Script Activity and writing results to a Data Extension.

How it works:

<script runat="server">
Platform.Load("Core", "1.1.1");
try {
  var req = new Script.Util.HttpRequest("https://api.example.com/leads");
  req.emptyContentHandling = 0;
  req.retryCount = 1;
  req.setHeader("Authorization", "Bearer MyToken");
  req.method = "GET";
  var resp = req.send();
  if (resp.statusCode != 200) {
    throw new Error("HTTP error: " + resp.statusCode);
  }
  var data = Platform.Function.ParseJSON(resp.content);
  // process data...
  Write("Retrieved " + data.length + " leads.");
} catch(e) {
  Write("FAILED: " + e.message);
  // log to error DE if needed
}
</script>

Decision Guidance

SituationRecommended ApproachReason
Retrieve/upsert Marketing Cloud objects (Subscribers, DEs, Sends)WSProxyHandles auth, paging, SOAP envelope automatically; lower overhead than raw HTTP
Call external REST API from Script ActivityScript.Util.HttpRequestDesigned for outbound HTTP; handles headers, methods, retries
Per-subscriber email personalization at send timeAMPscriptAMPscript has subscriber context; SSJS does not run per-subscriber during sends
Complex data transformation, looping, conditional logic in AutomationSSJS Script ActivitySSJS supports full procedural logic; AMPscript is template-oriented
Debugging a failing Script ActivityWrite() + try/catchWrite() outputs to Activity log; uncaught exceptions give no diagnostic info
Need to call a SOAP API without WSProxyScript.Util.HttpRequest + raw XMLLast resort only — WSProxy is always preferred for SOAP

Step-by-step instructions for an AI agent or practitioner working on this task:

  1. Confirm execution context — identify whether this is a Script Activity, Cloud Page, or Landing Page. Script Activity: check timeout risk (30-min limit). Cloud Page: check whether code needs to be synchronous and handle subscriber context.
  2. Identify the API target — if interacting with Marketing Cloud objects (Data Extensions, Subscribers, Sends), plan to use WSProxy. If calling external endpoints, plan to use Script.Util.HttpRequest.
  3. Author the SSJS block — wrap all code in <script runat="server"> tags. Load the Core library with Platform.Load("Core", "1.1.1"). Use only var for declarations. Use Stringify() and Platform.Function.ParseJSON() instead of JSON.stringify/JSON.parse.
  4. Wrap in try/catch — every meaningful operation (API call, WSProxy call, data write) must be inside a try/catch block with Write() logging in the catch. Never allow uncaught exceptions in Script Activities.
  5. Implement paging if using WSProxy retrieve — check result.HasMoreRows after every prox.retrieve() call and loop with prox.getNextPage() until false to avoid silently missing records.
  6. Test incrementally — run the Script Activity manually in Automation Studio, check the Activity log tab for Write() output, verify DE row counts before and after.
  7. Review for ES3 compatibility — scan for let, const, arrow functions, template literals, JSON.stringify, JSON.parse, Promise, and async/await — all are unsupported and will cause syntax or runtime errors.

Review Checklist

Run through these before marking work in this area complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Script Activity 30-minute timeout is absolute — If a Script Activity exceeds 30 minutes of execution time, it terminates without completing and the step is marked as an error. There is no graceful shutdown callback. For large-volume operations, split work across multiple Activities or use Query Activities for set-based data operations instead.
  2. WSProxy retrieve does not return all rows by default — prox.retrieve() returns a paged result set. If you do not check result.HasMoreRows and call prox.getNextPage(), you silently process only the first page of results (typically 2,500 rows). This causes data processing gaps that are very hard to detect.
  3. AMPscript is evaluated before SSJS — In a mixed file, AMPscript variable substitution happens before the SSJS engine runs. This means AMPscript @variables can inject values into SSJS string literals, but SSJS variables cannot be read by AMPscript in the same file. Relying on SSJS output being available to AMPscript in the same render pass will not work.

Output Artifacts

ArtifactDescription
SSJS Script Activity fileComplete <script runat="server"> block ready to paste into an Automation Studio Script Activity
WSProxy retrieve/upsert snippetParameterized code block for reading from or writing to a Data Extension via WSProxy
HTTP request snippetScript.Util.HttpRequest pattern for outbound REST calls with header, method, and error handling
Error logging patterntry/catch + Write() template for any Script Activity block

Package files