The List Core library object provides an object-oriented interface for SFMC publication lists: static methods create or look up lists, an initialized instance can delete itself, and the Subscribers namespace manages membership on that instance.

Methods

Method Returns Description
List.Init(key) ListInstance Bind to a list by external key
List.Add(properties) ListInstance Create a new list from properties
List.Retrieve(filter) object[] Query lists with a filter
<ListInstance>.Remove() string Delete the list represented by the instance
<ListInstance>.Subscribers.* Add, retrieve, unsubscribe, update, upsert subscribers (see dedicated page)

List.Init

Initializes a list instance using the list external key. Required before calling instance methods such as Remove() or Subscribers.*.

Syntax

List.Init(key)

Parameters

Name Type Required Description
key string Yes External key of the publication list

Return value

ListInstance — object bound to that list.

Examples

Platform.Load("core", "1");
var myList = List.Init("myList");
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: List.Init(key)
 *
 * CloudPage GET context. Proves:
 *   1. List requires the Core load and is then an object exposing the
 *      documented statics Init, Add and Retrieve.
 *   2. There is no static Update / Remove — those are instance methods.
 *   3. Init(key) returns a ListInstance: an object whose documented members
 *      are Remove (function) and Subscribers (object with Add / Retrieve /
 *      Update / Upsert / Unsubscribe / Tracking.Retrieve).
 *   4. The instance carries NO readable list fields (CustomerKey / Name /
 *      ListName / ID read back undefined) — Init binds a key, it does not
 *      fetch the record.
 *   5. A nonsense key yields an indistinguishable stub.
 *   6. The page example shape Init("myList") returns without throwing.
 *
 * NON-ASSERTABLE here: Subscribers method behaviour (dedicated page).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function typeOf(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW"; }
}
function invocationResult(fn) {
    try { fn(); return "returned"; } catch (ex) { return "threw"; }
}

assert("typeof List is object", typeOf(function () { return typeof List; }), "object");
assert("typeof List.Init is function", typeOf(function () { return typeof List.Init; }), "function");
assert("typeof List.Add is function", typeOf(function () { return typeof List.Add; }), "function");
assert("typeof List.Retrieve is function", typeOf(function () { return typeof List.Retrieve; }), "function");
assert("List.Update is not a static", typeOf(function () { return typeof List.Update; }), "undefined");
assert("List.Remove is not a static", typeOf(function () { return typeof List.Remove; }), "undefined");

var myList = List.Init("ssjs-guide-ts-list-init");
assert("typeof List.Init(key) is object", typeof myList, "object");
assert("typeof instance.Remove is function", typeof myList.Remove, "function");
assert("typeof instance.Subscribers is object", typeof myList.Subscribers, "object");
assert("typeof Subscribers.Add is function", typeof myList.Subscribers.Add, "function");
assert("typeof Subscribers.Retrieve is function", typeof myList.Subscribers.Retrieve, "function");
assert("typeof Subscribers.Update is function", typeof myList.Subscribers.Update, "function");
assert("typeof Subscribers.Upsert is function", typeof myList.Subscribers.Upsert, "function");
assert("typeof Subscribers.Unsubscribe is function", typeof myList.Subscribers.Unsubscribe, "function");
assert("typeof Subscribers.Tracking.Retrieve is function", typeof myList.Subscribers.Tracking.Retrieve, "function");
assert("instance has no Add (Add is static)", typeof myList.Add, "undefined");
assert("instance has no Retrieve (Retrieve is static)", typeof myList.Retrieve, "undefined");

assert("instance.CustomerKey is undefined (Init does not fetch)", typeof myList.CustomerKey, "undefined");
assert("instance.Name is undefined (Init does not fetch)", typeof myList.Name, "undefined");
assert("instance.ListName is undefined (Init does not fetch)", typeof myList.ListName, "undefined");
assert("instance.ID is undefined (Init does not fetch)", typeof myList.ID, "undefined");

var bogus = List.Init("ssjs-guide-no-such-list-zzz");
assert("Init(nonsense key) still returns an object", typeof bogus, "object");
assert("Init(nonsense key) exposes Remove", typeof bogus.Remove, "function");
assert("Init(nonsense key) exposes Subscribers", typeof bogus.Subscribers, "object");
assert("Init(nonsense key) stub is indistinguishable", ("" + Stringify(bogus)) === ("" + Stringify(myList)) ? "true" : "false", "true");
assert("page example: Init('myList') returns", invocationResult(function () { return List.Init("myList"); }), "returned");
</script>


List.Add

Creates a new list from the supplied JSON properties (CustomerKey, Name, Description, …). Unlike many Core Add methods, this returns an initialized ListInstance, not "OK".

Syntax

List.Add(properties)

Parameters

Name Type Required Description
properties object Yes Object describing the new list

Return value

ListInstance — handle for the newly created list.

Examples

Platform.Load("core", "1.1.5");
var myNewList = List.Add({
    CustomerKey: "libList",
    Name: "testLib",
    Description: "desc"
});
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: List.Add(properties)
 *
 * CloudPage GET context. Proves:
 *   1. List.Add is a function taking one properties object.
 *   2. Add(properties) with the page example fields (CustomerKey, Name,
 *      Description) creates a retrievable publication list.
 *   3. Unlike most Core Add methods, the return value is a ListInstance
 *      (object exposing Remove + Subscribers), NOT the string "OK" —
 *      matching the page Return value section.
 *   4. The returned instance is usable immediately: Remove on it returns
 *      "OK" after a control Retrieve proves the row exists (cleanup path
 *      recreates then removes again for the final re-count).
 *   5. Negative cases: Add() with no argument, Add({}), and re-using an
 *      existing CustomerKey all throw.
 *
 * FIXTURE: creates ssjs-guide-ts-list-add and removes it again at the end.
 * Cleanup is verified by re-counting Retrieve rows (ghost-record rule).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function invocationResult(fn) {
    try { fn(); return "returned"; } catch (ex) { return "threw"; }
}
function countByKey(key) {
    var rows = List.Retrieve({ Property: "CustomerKey", SimpleOperator: "equals", Value: key });
    return rows && rows.length ? rows.length : 0;
}

var KEY = "ssjs-guide-ts-list-add";
List.Init(KEY).Remove();
assert("precondition: no list under the probe key", "" + countByKey(KEY), "0");

assert("typeof List.Add is function", typeof List.Add, "function");
assert("Init instance has no Add (Add is static)", typeof List.Init(KEY).Add, "undefined");

var props = {
    CustomerKey: KEY,
    Name: "SSJS Guide TS List Add",
    Description: "desc"
};
var myList = null;
assert("Add(properties) does not throw", invocationResult(function () { myList = List.Add(props); }), "returned");
assert("typeof Add() result is object (page: ListInstance, not \"OK\")", typeof myList, "object");
assert("Add() result is not the string OK", myList === "OK" ? "true" : "false", "false");
assert("returned instance exposes Remove", typeof myList.Remove, "function");
assert("returned instance exposes Subscribers", typeof myList.Subscribers, "object");
assert("the list exists after Add", "" + countByKey(KEY), "1");

assert("Add() with no argument throws", invocationResult(function () { return List.Add(); }), "threw");
assert("Add({}) with an empty object throws", invocationResult(function () { return List.Add({}); }), "threw");
assert("Add() re-using an existing CustomerKey throws", invocationResult(function () { return List.Add(props); }), "threw");

assert("fixture cleanup removed the created list", "" + List.Init(KEY).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(KEY), "0");
</script>


List.Retrieve

Returns array of list objects matching the WSProxy-style filter.

Syntax

List.Retrieve(filter)

Parameters

Name Type Required Description
filter object Yes { Property, SimpleOperator, Value } (or compatible compound filter)

Return value

object[] — matching lists.

Examples

Platform.Load("core", "1.1.5");
var lists = List.Retrieve({
    Property: "ListName",
    SimpleOperator: "equals",
    Value: "BirthdayList"
});
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: List.Retrieve(filter)
 *
 * CloudPage GET context. Proves:
 *   1. Retrieve is a function taking one PascalCase WSProxy-style filter.
 *   2. On a match the result reports as [object Array], exposes .length,
 *      and is not a JS Array (instanceof Array is false).
 *   3. A matched row exposes readable CustomerKey, ListName, ID, Description.
 *   4. On no match the same array-like shape is returned with length 0
 *      and Stringify "[]".
 *   5. The page example filter Property "ListName" resolves the owned
 *      fixture; CustomerKey filter also resolves it.
 *
 * FIXTURE: creates ssjs-guide-ts-list-retrieve and removes it at the end.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function countByKey(key) {
    var rows = List.Retrieve({ Property: "CustomerKey", SimpleOperator: "equals", Value: key });
    return rows && rows.length ? rows.length : 0;
}

var KEY = "ssjs-guide-ts-list-retrieve";
var NAME = "SSJS Guide TS List Retrieve";
List.Init(KEY).Remove();
List.Add({ CustomerKey: KEY, Name: NAME, Description: "retrieve probe" });

assert("typeof List.Retrieve is function", typeof List.Retrieve, "function");

var rows = List.Retrieve({ Property: "CustomerKey", SimpleOperator: "equals", Value: KEY });
assert("typeof the matched result is object", typeof rows, "object");
assert("the matched result reports as [object Array]", Object.prototype.toString.call(rows), "[object Array]");
assert("the matched result exposes a numeric .length", typeof rows.length, "number");
assert("the matched result has exactly one row", "" + rows.length, "1");
assert("instanceof Array is false (engine-wide host-array quirk)", rows instanceof Array ? "true" : "false", "false");
assert("matched row CustomerKey equals the filter value", "" + rows[0].CustomerKey, KEY);
assert("matched row ListName is a string", typeof rows[0].ListName, "string");
assert("matched row ListName equals Name used at Add", "" + rows[0].ListName, NAME);
assert("matched row ID is a number", typeof rows[0].ID, "number");
assert("matched row Description is a string", typeof rows[0].Description, "string");

var byName = List.Retrieve({ Property: "ListName", SimpleOperator: "equals", Value: NAME });
assert("page example: ListName filter finds the fixture", "" + (byName && byName.length ? byName.length : 0), "1");
assert("page example: ListName row CustomerKey matches", "" + byName[0].CustomerKey, KEY);

var empty = List.Retrieve({ Property: "CustomerKey", SimpleOperator: "equals", Value: "ssjs-guide-no-such-list-zzz" });
assert("empty result reports as [object Array]", Object.prototype.toString.call(empty), "[object Array]");
assert("empty result length is 0", "" + empty.length, "0");
assert("empty result Stringify is []", "" + Stringify(empty), "[]");
assert("workaround: guard with rows && rows.length before indexing", (rows && rows.length) ? "true" : "false", "true");

assert("fixture cleanup removed the created list", "" + List.Init(KEY).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(KEY), "0");
</script>


<ListInstance>.Remove

Deletes the list bound to this instance (the publication list itself).

Syntax

<ListInstance>.Remove()

Return value

"OK" on success. A nonexistent key returns the plain string "Error" (does not throw).

Examples

Platform.Load("core", "1.1.5");
var myList = List.Init("myList");
var status = myList.Remove();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: <ListInstance>.Remove()
 *
 * CloudPage GET context. Proves:
 *   1. Remove is an instance method (typeof "function") and takes no args.
 *   2. Remove() returns the string "OK" on success.
 *   3. The deletion really happened: after Remove, Retrieve count is 0.
 *   4. DEV: Remove() on a nonexistent key returns the plain string "Error"
 *      rather than throwing (official docs / earlier guide prose: throws).
 *      Callers MUST test the return value; try/catch alone is not enough.
 *      This is also why other scripts can call Remove as an unconditional
 *      orphan-cleanup preamble.
 *
 * FIXTURE: creates ssjs-guide-ts-list-remove and deletes it as the assert.
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function invocationResult(fn) {
    try { fn(); return "returned"; } catch (ex) { return "threw"; }
}
function countByKey(key) {
    var rows = List.Retrieve({ Property: "CustomerKey", SimpleOperator: "equals", Value: key });
    return rows && rows.length ? rows.length : 0;
}

var KEY = "ssjs-guide-ts-list-remove";
List.Init(KEY).Remove();
List.Add({
    CustomerKey: KEY,
    Name: "SSJS Guide TS List Remove",
    Description: "remove probe"
});

var myList = List.Init(KEY);

assert("typeof instance.Remove is function", typeof myList.Remove, "function");
assert("control: fixture count is 1 before Remove", "" + countByKey(KEY), "1");

var status = myList.Remove();
assert("page example: Remove() returns \"OK\"", "" + status, "OK");
assert("Remove returns a string", typeof status, "string");

assert("after Remove the Retrieve count is 0", "" + countByKey(KEY), "0");
assert("after Remove a second Remove returns \"Error\"", "" + List.Init(KEY).Remove(), "Error");

assert("DEV Remove on a nonexistent key returns \"Error\" (docs: throws)", "" + List.Init("ssjs-guide-no-such-list-zzz").Remove(), "Error");
assert("DEV Remove on a nonexistent key does NOT throw (docs: throws)", invocationResult(function () { return List.Init("ssjs-guide-no-such-list-zzz").Remove(); }), "returned");
</script>


Subscribers

Subscriber membership operations are invoked on list.Subscribers after List.Init. See List.Subscribers for Add, Retrieve, Unsubscribe, Update, Upsert, and Subscribers.Tracking.Retrieve.


Subscribe / unsubscribe pattern

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

var action = Platform.Request.GetFormField("action"); // "subscribe" or "unsubscribe"
var email = Platform.Request.GetFormField("email");
var listKey = "Newsletter_PublicList";

if (!Platform.Function.IsEmailAddress(email)) {
    Write(Stringify({ status: 400, statusMessage: "Bad Request", error: "Invalid email" }));
} else {
    var list = List.Init(listKey);
    try {
        if (action === "subscribe") {
            list.Subscribers.Upsert(email, {
                SubscribedAt: Platform.Function.Now()
            });
            Write(Stringify({ status: "subscribed" }));
        } else if (action === "unsubscribe") {
            list.Subscribers.Unsubscribe(email);
            Write(Stringify({ status: "unsubscribed" }));
        }
    } catch (e) {
        Write(Stringify({ status: 500, statusMessage: "Internal Server Error", error: e.message }));
    }
}

See also