Skip to main content

Basics — Marketing Cloud SSJS fundamentals

Where SSJS runs in Marketing Cloud, what runtime you actually have (SpiderMonkey 1.7), and the two main contexts — CloudPage rendering vs Automation Script Activity — that decide the patterns you reach for.

Reference·Last updated 2026-05-08·Drafted by Lira · Edited by German Medina

SSJS (Server-Side JavaScript) is the scripting language Marketing Cloud uses where AMPscript can't reach: dynamic CloudPages, complex callouts, multi-step orchestration in Automations, programmatic DE manipulation. It runs on SpiderMonkey 1.7 — the JavaScript engine from 2007 — with a Salesforce-specific Platform.* API layered on top. Most of what you write is the Platform API; the JS underneath is mostly plumbing.

This page is the orientation. The rest of the SSJS catalog (Platform, WSProxy, function categories, Style Guide) builds on this mental model.

Official syntax

A SSJS block lives inside <script runat="server">...</script> tags. It can appear in three contexts:

<!-- 1. CloudPage — rendered HTML output -->
<!DOCTYPE html>
<html>
<body>
  <script runat="server">
    Platform.Load("Core", "1.1.5");
    var name = Request.GetQueryStringParameter("name");
    Write("<h1>Hello, " + name + "</h1>");
  </script>
</body>
</html>

<!-- 2. Automation Studio Script Activity — runs in a scheduled Automation -->
<script runat="server">
Platform.Load("Core", "1.1.5");
var subscriberKey = "12345";
var rows = Platform.Function.LookupRows(
  "master_subscribers",
  "SubscriberKey",
  subscriberKey
);
Platform.Function.UpsertData(
  "de_log_runs",
  ["RunId"], [Platform.Function.GUID()],
  ["Step","Ts"], ["lookup", Now()]
);
</script>

<!-- 3. Email Studio script block — rare, mostly for AMPscript-too-complex cases -->
<script runat="server" language="javascript">
Platform.Load("Core", "1.1.5");
var personalized = Variable.GetValue("@firstName") + ", welcome";
</script>

The first line of every SSJS block should be Platform.Load("Core", "1.1.5"). Without it, the entire Platform.* namespace is undefined and the script fails with a confusing Platform is not defined error (see gotchas — #2).

The supported language surface:

| Feature | Available | |---|---| | var, function, traditional loops | ✓ | | Math, basic String / Array / Date methods (ES5-era) | ✓ | | try / catch / throw | ✓ | | Regular expressions | ✓ | | let / const | ✗ (SyntaxError) | | Arrow functions | ✗ | | Template literals | ✗ | | Destructuring / spread / rest | ✗ | | Promise / async / await | ✗ | | JSON.parse / JSON.stringify | ✗ in older tenants — use Platform.Function.ParseJSON() | | console.log | ✗ — use Write() (CloudPage) or DE log writes (Automation) |

Reference:

What survives in production

Two contexts, two mental models

The same SSJS code behaves differently depending on where it runs. Get the mental model right before writing the script.

CloudPage SSJS:

  • Output goes to the rendered HTML via Write() (and automatic output of literal HTML between script blocks).
  • A user clicked a link; you have ~30 seconds before the browser gives up (well below the 30-min hard limit, but UX matters).
  • Errors render to the page itself unless you trap them.
  • Stateless per request — no session unless you build one with cookies + DE storage.

Automation Studio Script Activity:

  • No output anyone sees. All "logging" is writes to a de_log_* Data Extension.
  • 30-minute hard timeout (see gotchas — #3).
  • Runs as part of an Automation chain (typically: SQL Query → Script → Send → Log).
  • Errors fail the Activity step but don't propagate to the next step unless you read the Activity status and react.

The patterns you reach for differ. A CloudPage uses Write() for output; a Script Activity logs to a DE. A CloudPage handles user input via Request.GetQueryStringParameter(); a Script Activity reads inputs from a "config" DE that the Automation upstream wrote.

Platform.* is the API; everything else is plumbing

The Salesforce-specific API namespace (Platform.*) is what gets you actual work done. Plain JS gives you control flow, math, loops; Platform.Function.* gives you DE Lookup/Add/Update/Delete, GUID generation, encryption, JSON parsing, current date, and the rest. Almost every interesting SSJS line is a Platform.Function.* call.

Platform.Load("Core", "1.1.5");

// Plain JS — control flow
var subKey = "12345";
var status = "Active";

// Platform.* — actual work
var rows = Platform.Function.LookupRows("master_subs", "SubscriberKey", subKey);
if (rows.length > 0) {
  Platform.Function.UpdateData(
    "master_subs",
    ["SubscriberKey"], [subKey],
    ["Status", "UpdatedAt"], [status, Now()]
  );
} else {
  Platform.Function.InsertData(
    "master_subs",
    ["SubscriberKey"], [subKey],
    ["Status", "CreatedAt"], [status, Now()]
  );
}

The reference for which Platform.Function.* calls exist lives at the link above and on the dedicated reference page in this catalog.

WSProxy for SOAP API access — when Platform.Function.* isn't enough

Platform.Function.* is a high-level wrapper around a subset of the SOAP API. When you need the full surface — bulk operations, retrieving Salesforce objects beyond the wrapper's scope, async operations — you drop down to WSProxy.

Platform.Load("Core", "1.1.5");
var prox = new Script.Util.WSProxy();

// Retrieve subscribers via SOAP
var result = prox.retrieve("Subscriber", ["EmailAddress", "Status"], {
  Property: "SubscriberKey",
  SimpleOperator: "equals",
  Value: "12345"
});

WSProxy has its own auth-token-expiration trap (see gotchas — #8) and is documented in the upcoming WSProxy reference page.

Output: Write() for CloudPages, DE writes for Automations

There is no console. The two output mechanisms are:

  • Write("text") — appends to the page output (CloudPage) or to the Activity's Output (Automation, but rarely visible)
  • Platform.Function.UpsertData("de_log_*", ...) — writes a row to a log DE you control

The Cleon convention: every production SSJS writes to a de_log_ssjs_runs Data Extension at every meaningful step (start, lookup completed, write completed, end), with a RunId (typically Platform.Function.GUID()), Step name, Message, and Ts. When something looks off, you query the log instead of trying to reconstruct from logs that don't exist.

Quick decision

Reach for SSJS Script Activity when:

  • You need control flow that SQL can't express (per-row branching, callouts, multi-step orchestration).
  • You need to call an external API and react to the response.
  • You need to combine multiple Data Extension reads + writes in one logical operation.

Reach for SSJS in a CloudPage when:

  • You're building a dynamic landing page (preference center, suppression form, dynamic content).
  • The page needs to read or write Data Extensions based on URL parameters.
  • AMPscript hits its limits (complex iteration, conditional logic that gets messy).

Reach for SQL Query Activity instead when:

  • The work is bulk read + transform + write across a Data Extension. SQL is faster and clearer for set-based operations.

Reach for AMPscript instead when:

  • The work is per-message personalization at send time. AMPscript runs inline in the email, SSJS doesn't.

Don't reach for SSJS when:

  • You actually need real concurrency / async — SSJS is single-threaded synchronous (see gotchas — #10). Split the work across parallel Script Activities orchestrated by Automation Studio instead.

Related

More SSJS reference pages incoming: Platform Functions · WSProxy · Variable + Util · String / Date / Util functions · Style Guide.

Plus how-to snippets for common production patterns — DE add/update/upsert, error handling, callout pagination, etc.