Subscriber
Core library object for managing All Subscribers list entries — add, retrieve, upsert, update, remove, unsubscribe, and retrieve attributes and lists.
- SSJS
Subscriber- SOAP
Subscriber- mcdev
- not supported
- GUI
- Subscriber
The Subscriber Core library object manages entries in the All Subscribers list. Use it to create, look up, update, or unsubscribe subscribers, and to retrieve their attributes and list memberships.
Requires Platform.Load("core", "1.1.5") before use.
Methods
| Method | Returns | Description |
|---|---|---|
Subscriber.Init(key) |
SubscriberInstance | Initialize a Subscriber instance by key |
Subscriber.Add(properties) |
string | Create a new subscriber |
Subscriber.Retrieve(filter) |
object[] | Retrieve subscribers matching a filter |
<SubscriberInstance>.Upsert(properties) |
string | Create or update the initialized subscriber |
<SubscriberInstance>.Statistics() |
object | Retrieve statistics for the initialized subscriber |
<SubscriberInstance>.Update(properties) |
string | Update the initialized subscriber |
<SubscriberInstance>.Remove() |
string | Delete the initialized subscriber |
<SubscriberInstance>.Unsubscribe() |
string | Set the subscriber status to Unsubscribed |
<SubscriberInstance>.Attributes.Retrieve() |
object[] | Retrieve attributes for the subscriber |
<SubscriberInstance>.Lists.Retrieve() |
object[] | Retrieve list memberships for the subscriber |
Subscriber.Init
Initializes a Subscriber instance bound to the specified subscriber key. Required before invoking any instance method on the returned object.
Syntax
Subscriber.Init(key)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Subscriber key |
Return value
SubscriberInstance
Examples
Platform.Load("core", "1");
var sub = Subscriber.Init("mySubscriber");
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Subscriber.Init(key)
*
* CloudPage GET context. Proves:
* 1. Subscriber requires the Core load and is then an object exposing
* the documented statics Init, Add and Retrieve.
* 2. The static Upsert / Statistics are undefined — those live on the
* Init instance (covered in their chapters).
* 3. Init(key) returns a SubscriberInstance object exposing Upsert,
* Statistics, Update, Remove, Unsubscribe, Attributes.Retrieve and
* Lists.Retrieve.
* 4. The instance carries NO readable subscriber fields (SubscriberKey /
* EmailAddress / Status read back undefined) — Init binds a key.
* 5. A nonsense key yields an indistinguishable stub.
* 6. The page example shape Init("mySubscriber") returns without throwing.
*
* 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 Subscriber is object", typeOf(function () { return typeof Subscriber; }), "object");
assert("typeof Subscriber.Init is function", typeOf(function () { return typeof Subscriber.Init; }), "function");
assert("typeof Subscriber.Add is function", typeOf(function () { return typeof Subscriber.Add; }), "function");
assert("typeof Subscriber.Retrieve is function", typeOf(function () { return typeof Subscriber.Retrieve; }), "function");
assert("Subscriber.Upsert is not a static", typeOf(function () { return typeof Subscriber.Upsert; }), "undefined");
assert("Subscriber.Statistics is not a static", typeOf(function () { return typeof Subscriber.Statistics; }), "undefined");
assert("Subscriber.Update is not a static", typeOf(function () { return typeof Subscriber.Update; }), "undefined");
assert("Subscriber.Remove is not a static", typeOf(function () { return typeof Subscriber.Remove; }), "undefined");
assert("Subscriber.Unsubscribe is not a static", typeOf(function () { return typeof Subscriber.Unsubscribe; }), "undefined");
var sub = Subscriber.Init("ssjs-guide-ts-sub-init");
assert("typeof Subscriber.Init(key) is object", typeof sub, "object");
assert("typeof instance.Upsert is function", typeof sub.Upsert, "function");
assert("typeof instance.Statistics is function", typeof sub.Statistics, "function");
assert("typeof instance.Update is function", typeof sub.Update, "function");
assert("typeof instance.Remove is function", typeof sub.Remove, "function");
assert("typeof instance.Unsubscribe is function", typeof sub.Unsubscribe, "function");
assert("typeof instance.Attributes is object", typeof sub.Attributes, "object");
assert("typeof Attributes.Retrieve is function", typeof sub.Attributes.Retrieve, "function");
assert("typeof instance.Lists is object", typeof sub.Lists, "object");
assert("typeof Lists.Retrieve is function", typeof sub.Lists.Retrieve, "function");
assert("instance has no Add (Add is static)", typeof sub.Add, "undefined");
assert("instance has no Retrieve (Retrieve is static)", typeof sub.Retrieve, "undefined");
assert("instance.SubscriberKey is undefined (Init does not fetch)", typeof sub.SubscriberKey, "undefined");
assert("instance.EmailAddress is undefined (Init does not fetch)", typeof sub.EmailAddress, "undefined");
assert("instance.Status is undefined (Init does not fetch)", typeof sub.Status, "undefined");
var bogus = Subscriber.Init("ssjs-guide-no-such-sub-zzz");
assert("Init(nonsense key) still returns an object", typeof bogus, "object");
assert("Init(nonsense key) exposes Upsert", typeof bogus.Upsert, "function");
assert("Init(nonsense key) stub is indistinguishable", ("" + Stringify(bogus)) === ("" + Stringify(sub)) ? "true" : "false", "true");
assert("page example: Init('mySubscriber') returns", invocationResult(function () { return Subscriber.Init("mySubscriber"); }), "returned");
</script>
Subscriber.Add
Runtime-verified against a Parent BU session: Subscriber.Add({EmailAddress, SubscriberKey}) returns the string "OK" and the new subscriber is retrievable afterwards. When the EmailAddress is on a spam-blocked domain (for example @example.com) the call returns the string "Error" instead — the underlying WSProxy create reports ErrorCode 12002 (TriggeredSpamFilter). Use a deliverable domain when testing.
Creates a new subscriber from the supplied properties.
Syntax
Subscriber.Add(properties)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
properties |
object | Yes | EmailAddress, SubscriberKey, EmailTypePreference, Attributes, Lists, … — see Writing attributes for the required Attributes shape |
Return value
"OK" on success, or "Error" when the create is rejected (for example a spam-blocked EmailAddress domain).
Examples
Platform.Load("core", "1.1.5");
var newSubscriber = {
EmailAddress: "test.008@example.com",
SubscriberKey: "20100730001",
EmailTypePreference: "Text",
Attributes: { "First Name": "test.008", "Last Name": "test.008" },
Lists: { Status: "Active", ID: 12345, Action: "Create" }
};
var status = Subscriber.Add(newSubscriber);
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Subscriber.Add(properties)
*
* CloudPage GET context. Proves:
* 1. Add is a function taking one properties object.
* 2. Add({EmailAddress, SubscriberKey}) with a deliverable @gmail.com
* address returns "OK" and the row is retrievable afterwards.
* 3. Add with a spam-blocked @example.com address returns "Error"
* (TriggeredSpamFilter) and does not create a retrievable row.
* 4. Negative: Add() / Add({}) return "Error" without throwing.
*
* FIXTURE: ssjs.guide.ts.sub.add@gmail.com / ssjs-guide-ts-sub-add.
* 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 nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-add";
var EMAIL = "ssjs.guide.ts.sub.add@gmail.com";
var SPAM_SK = "ssjs-guide-ts-sub-add-spam";
var SPAM_EMAIL = "ssjs.guide.ts.sub.add@example.com";
nukeSub(SK);
nukeSub(SPAM_SK);
assert("precondition: no fixture under gmail key", "" + countByKey(SK), "0");
assert("typeof Subscriber.Add is function", typeof Subscriber.Add, "function");
assert("Add(gmail) returns \"OK\"", "" + Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK }), "OK");
assert("row exists after Add", "" + countByKey(SK), "1");
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: SK });
assert("row EmailAddress matches", "" + rows[0].EmailAddress, EMAIL);
assert("row SubscriberKey matches", "" + rows[0].SubscriberKey, SK);
assert("Add(example.com) returns \"Error\" (TriggeredSpamFilter)", "" + Subscriber.Add({ EmailAddress: SPAM_EMAIL, SubscriberKey: SPAM_SK }), "Error");
assert("Add(example.com) does NOT throw", invocationResult(function () { return Subscriber.Add({ EmailAddress: SPAM_EMAIL, SubscriberKey: SPAM_SK }); }), "returned");
assert("spam Add created no retrievable row", "" + countByKey(SPAM_SK), "0");
assert("DEV Add() returns \"Error\"", "" + Subscriber.Add(), "Error");
assert("DEV Add() does NOT throw", invocationResult(function () { return Subscriber.Add(); }), "returned");
assert("DEV Add({}) returns \"Error\"", "" + Subscriber.Add({}), "Error");
assert("DEV Add({}) does NOT throw", invocationResult(function () { return Subscriber.Add({}); }), "returned");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
Subscriber.Retrieve
Returns an array of subscribers matching the specified filter.
Syntax
Subscriber.Retrieve(filter)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
filter |
object | Yes | PascalCase WSProxy-style filter object: {Property, SimpleOperator, Value} |
Return value
object[]
Examples
Platform.Load("core", "1.1.5");
var results = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: "MySubscriberKey" });
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Subscriber.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] with .length;
* instanceof Array is false (engine-wide host-array quirk).
* 3. SubscriberKey filter finds the fixture row fields.
* 4. Empty match returns length 0 / Stringify "[]".
*
* FIXTURE: ssjs-guide-ts-sub-ret / ssjs.guide.ts.sub.ret@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-ret";
var EMAIL = "ssjs.guide.ts.sub.ret@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
assert("typeof Subscriber.Retrieve is function", typeof Subscriber.Retrieve, "function");
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: SK });
assert("matched result reports as [object Array]", Object.prototype.toString.call(rows), "[object Array]");
assert("matched result length is 1", "" + rows.length, "1");
assert("instanceof Array is false (host-array quirk)", rows instanceof Array ? "true" : "false", "false");
assert("matched row SubscriberKey", "" + rows[0].SubscriberKey, SK);
assert("matched row EmailAddress", "" + rows[0].EmailAddress, EMAIL);
assert("matched row Status is a string", typeof rows[0].Status, "string");
var empty = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: "ssjs-guide-no-such-sub-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("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Upsert
The official example passes Attributes as an array of { Name, Value } pairs. At runtime that form stores nothing: the call still returns "OK", but a read-back through Attributes.Retrieve() shows the value unchanged, so the failure is silent. Attributes must be a plain object keyed by attribute name — see Writing attributes.
Runtime-verified against a Parent BU session: Subscriber.Init(key).Upsert({EmailAddress}) returns the string "OK" and the subscriber is retrievable by its key afterwards.
Creates a new subscriber, or updates the initialized one matched by EmailAddress / SubscriberKey.
Syntax
<SubscriberInstance>.Upsert(properties)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
properties |
object | Yes | EmailAddress, SubscriberKey, Attributes, … — see Writing attributes for the required Attributes shape |
Return value
"OK" on success.
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("test@example.com");
var result = subObj.Upsert({
EmailAddress: "test@example.com",
SubscriberKey: "test@example.com",
Attributes: { "First Name": "Jane" }
});
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Upsert(properties)
*
* CloudPage GET context. Proves:
* 1. The static Subscriber.Upsert is undefined.
* 2. Upsert is a function on the Init instance.
* 3. Init(key).Upsert({EmailAddress, SubscriberKey}) returns "OK" and
* the subscriber is retrievable by key afterwards.
* 4. A second Upsert on the same key also returns "OK" (update path).
*
* FIXTURE: ssjs-guide-ts-sub-ups / ssjs.guide.ts.sub.ups@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-ups";
var EMAIL = "ssjs.guide.ts.sub.ups@gmail.com";
nukeSub(SK);
assert("typeof Subscriber.Upsert is undefined", typeof Subscriber.Upsert, "undefined");
var sub = Subscriber.Init(SK);
assert("typeof instance.Upsert is function", typeof sub.Upsert, "function");
assert("Upsert(properties) returns \"OK\"", "" + sub.Upsert({ EmailAddress: EMAIL, SubscriberKey: SK }), "OK");
assert("row exists after Upsert", "" + countByKey(SK), "1");
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: SK });
assert("row EmailAddress matches after Upsert", "" + rows[0].EmailAddress, EMAIL);
assert("second Upsert returns \"OK\"", "" + Subscriber.Init(SK).Upsert({ EmailAddress: EMAIL, SubscriberKey: SK }), "OK");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Statistics
Retrieves statistical data for the initialized subscriber (sends, opens, clicks, bounces, unsubscribes).
Syntax
<SubscriberInstance>.Statistics()
Return value
object — a single object with subscriber statistics (not an array).
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("test@example.com");
var stats = subObj.Statistics();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Statistics()
*
* CloudPage GET context. Proves:
* 1. The static Subscriber.Statistics is undefined.
* 2. Statistics is a function on the Init instance (no args).
* 3. Statistics() returns a single object (typeof "object"), not an array.
*
* NON-ASSERTABLE: send/open/click/bounce/unsub counters after a real send
* (no send performed; published scripts stay reader-safe).
*
* FIXTURE: ssjs-guide-ts-sub-stats / ssjs.guide.ts.sub.stats@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-stats";
var EMAIL = "ssjs.guide.ts.sub.stats@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
assert("typeof Subscriber.Statistics is undefined", typeof Subscriber.Statistics, "undefined");
var sub = Subscriber.Init(SK);
assert("typeof instance.Statistics is function", typeof sub.Statistics, "function");
var stats = sub.Statistics();
assert("Statistics() typeof is object", typeof stats, "object");
assert("Statistics() is not an array (page: single object)", Object.prototype.toString.call(stats) === "[object Array]" ? "true" : "false", "false");
assert("Statistics() is not null", stats === null ? "true" : "false", "false");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Update
Runtime-verified against a Parent BU session: on an existing subscriber both the 0-argument Update() and the object-argument Update({EmailAddress}) form return the string "OK".
Updates the previously initialized subscriber with the supplied attributes.
Syntax
<SubscriberInstance>.Update(properties)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
properties |
object | Yes | Subscriber properties to change — see Writing attributes for the required Attributes shape |
Return value
"OK" on success.
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("SubKey");
var status = subObj.Update({ EmailTypePreference: "HTML", Attributes: { "First Name": "Test", "Last Name": "User" } });
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Update(properties)
*
* CloudPage GET context. Proves:
* 1. Update is a function on the Init instance.
* 2. Zero-argument Update() returns "OK" on an existing subscriber.
* 3. Update({EmailAddress}) returns "OK".
* 4. Update with EmailTypePreference returns "OK".
*
* FIXTURE: ssjs-guide-ts-sub-upd / ssjs.guide.ts.sub.upd@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-upd";
var EMAIL = "ssjs.guide.ts.sub.upd@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
var sub = Subscriber.Init(SK);
assert("typeof instance.Update is function", typeof sub.Update, "function");
assert("Update() zero-arg returns \"OK\"", "" + sub.Update(), "OK");
assert("Update({EmailAddress}) returns \"OK\"", "" + sub.Update({ EmailAddress: EMAIL }), "OK");
assert("Update({EmailTypePreference:HTML}) returns \"OK\"", "" + sub.Update({ EmailTypePreference: "HTML" }), "OK");
assert("row still exists after Update", "" + countByKey(SK), "1");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Remove
The official docs state that a failure throws. It does not: removing a key that has no matching subscriber returns the plain string "Error" and execution continues.
Runtime-verified against a Parent BU session: Subscriber.Init(key).Remove() returns the string "OK" and a follow-up Subscriber.Retrieve by that key returns zero rows, confirming deletion.
Deletes the previously initialized subscriber.
Syntax
<SubscriberInstance>.Remove()
Return value
"OK" on success, or "Error" when the delete is rejected — for example when no subscriber matches the initialized key. It does not throw.
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("SubKey");
var status = subObj.Remove();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Remove()
*
* CloudPage GET context. Proves:
* 1. Remove is a function on the Init instance.
* 2. Remove() returns "OK" on an existing subscriber.
* 3. Follow-up Retrieve by that key returns zero rows.
* 4. DEV: Remove on a missing key returns "Error" without throwing
* (docs: returns "OK" or throws) — this is what makes it safe as an
* orphan-cleanup preamble.
*
* FIXTURE: ssjs-guide-ts-sub-rm / ssjs.guide.ts.sub.rm@gmail.com.
* 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 nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-rm";
var EMAIL = "ssjs.guide.ts.sub.rm@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
assert("precondition: fixture row exists", "" + countByKey(SK), "1");
var sub = Subscriber.Init(SK);
assert("typeof instance.Remove is function", typeof sub.Remove, "function");
assert("Remove() returns \"OK\"", "" + sub.Remove(), "OK");
assert("Retrieve length is 0 after Remove", "" + countByKey(SK), "0");
assert("DEV Remove(missing) returns \"Error\" (docs: returns \"OK\" or throws)", "" + Subscriber.Init("ssjs-guide-no-such-sub-zzz").Remove(), "Error");
assert("DEV Remove(missing) does NOT throw (docs: throws on failure)", invocationResult(function () { return Subscriber.Init("ssjs-guide-no-such-sub-zzz").Remove(); }), "returned");
</script>
<SubscriberInstance>.Unsubscribe
Runtime-verified against a Parent BU session: Subscriber.Init(key).Unsubscribe() returns the string "OK" and a follow-up Subscriber.Retrieve shows the subscriber’s Status is "Unsubscribed".
Sets the previously initialized subscriber’s status to "Unsubscribed".
Syntax
<SubscriberInstance>.Unsubscribe()
Return value
"OK" on success.
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("SubKey");
var status = subObj.Unsubscribe();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Unsubscribe()
*
* CloudPage GET context. Proves:
* 1. Unsubscribe is a function on the Init instance.
* 2. Unsubscribe() returns "OK".
* 3. Follow-up Retrieve shows Status "Unsubscribed" — the row remains
* (not deleted), matching All Subscribers unsubscribe semantics.
*
* FIXTURE: ssjs-guide-ts-sub-unsub / ssjs.guide.ts.sub.unsub@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-unsub";
var EMAIL = "ssjs.guide.ts.sub.unsub@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
var sub = Subscriber.Init(SK);
assert("typeof instance.Unsubscribe is function", typeof sub.Unsubscribe, "function");
assert("Unsubscribe() returns \"OK\"", "" + sub.Unsubscribe(), "OK");
var after = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: SK });
assert("row still present after Unsubscribe", "" + after.length, "1");
assert("Status is Unsubscribed after Unsubscribe", "" + after[0].Status, "Unsubscribed");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Attributes.Retrieve
Returns an array of attributes associated with the previously initialized subscriber.
Syntax
<SubscriberInstance>.Attributes.Retrieve()
Return value
object[]
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("SubKey");
var attributes = subObj.Attributes.Retrieve();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Attributes.Retrieve()
*
* CloudPage GET context. Proves:
* 1. Attributes.Retrieve is a function on the Init instance.
* 2. It returns an array-like ([object Array]) of attribute objects.
*
* NON-ASSERTABLE: exact Attribute Name/Value catalog (tenant-specific).
*
* FIXTURE: ssjs-guide-ts-sub-attr / ssjs.guide.ts.sub.attr@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-attr";
var EMAIL = "ssjs.guide.ts.sub.attr@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
var sub = Subscriber.Init(SK);
assert("typeof Attributes.Retrieve is function", typeof sub.Attributes.Retrieve, "function");
var attrs = sub.Attributes.Retrieve();
assert("Attributes.Retrieve reports as [object Array]", Object.prototype.toString.call(attrs), "[object Array]");
assert("Attributes.Retrieve exposes numeric .length", typeof attrs.length, "number");
assert("instanceof Array is false (host-array quirk)", attrs instanceof Array ? "true" : "false", "false");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
<SubscriberInstance>.Lists.Retrieve
Returns the lists the previously initialized subscriber is a member of.
Syntax
<SubscriberInstance>.Lists.Retrieve()
Return value
object[]
Examples
Platform.Load("core", "1.1.5");
var subObj = Subscriber.Init("SubKey");
var listArray = subObj.Lists.Retrieve();
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: <SubscriberInstance>.Lists.Retrieve()
*
* CloudPage GET context. Proves:
* 1. Lists.Retrieve is a function on the Init instance.
* 2. It returns an array-like ([object Array]) of list memberships
* (may be empty for a brand-new All Subscribers row with no
* publication-list membership).
*
* FIXTURE: ssjs-guide-ts-sub-lists / ssjs.guide.ts.sub.lists@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-lists";
var EMAIL = "ssjs.guide.ts.sub.lists@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
var sub = Subscriber.Init(SK);
assert("typeof Lists.Retrieve is function", typeof sub.Lists.Retrieve, "function");
var lists = sub.Lists.Retrieve();
assert("Lists.Retrieve reports as [object Array]", Object.prototype.toString.call(lists), "[object Array]");
assert("Lists.Retrieve exposes numeric .length", typeof lists.length, "number");
assert("Lists.Retrieve Stringify is a JSON array", ("" + Stringify(lists)).charAt(0), "[");
assert("instanceof Array is false (host-array quirk)", lists instanceof Array ? "true" : "false", "false");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
</script>
Writing attributes
The official examples are inconsistent about the Attributes container — Subscriber.Add and <SubscriberInstance>.Update show a plain object, <SubscriberInstance>.Upsert shows an array of { Name, Value } pairs. Runtime says only the object form works; the array form is accepted and returns "OK", but writes nothing.
Subscriber.Add, <SubscriberInstance>.Upsert and <SubscriberInstance>.Update all accept an Attributes key. It must be a plain object keyed by attribute name:
Platform.Load("core", "1.1.5");
Subscriber.Init("SubKey").Upsert({
EmailAddress: "test@example.com",
SubscriberKey: "SubKey",
Attributes: { "First Name": "Jane", "Last Name": "Doe" }
});
The array-of-pairs form is silently ignored on all three methods — the call returns "OK", no error is raised, and Attributes.Retrieve() afterwards shows the previous value:
// does NOT write anything, yet still returns "OK"
Subscriber.Init("SubKey").Upsert({
EmailAddress: "test@example.com",
SubscriberKey: "SubKey",
Attributes: [ { Name: "First Name", Value: "Jane" } ]
});
Because the array form fails silently, always confirm a write with a read-back through Attributes.Retrieve() when porting code that was written against the official Upsert example.
The keys are the attribute display names as configured on the business unit, so they commonly contain spaces. Spelling is not normalised: on the tested BU the attribute is "First Name", and the "FirstName" spelling used by the official Upsert example does not exist there at all — reading it back returns no entry. Since the attribute catalog is per-tenant, discover the available names with Attributes.Retrieve() rather than assuming them.
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: the Attributes payload shape
*
* CloudPage GET context. Proves which container the Attributes key accepts
* on Add / Upsert / Update. The return value alone settles nothing — all
* shapes return "OK" — so every write is followed by a READ-BACK through
* Attributes.Retrieve(). Proves:
* 1. A plain OBJECT keyed by attribute name ({ "First Name": "..." })
* writes the value: the read-back returns the sentinel.
* 2. DEV: the ARRAY of { Name, Value } pairs shown in the official
* Upsert example returns "OK" but writes NOTHING — the read-back is
* unchanged. The failure is silent, on Upsert, Update and Add alike.
* 3. The object form writes several attributes in one call.
* 4. Attribute names are the tenant's display names ("First Name" with a
* space on this BU); the doc's "FirstName" spelling is not present.
*
* The targeted attribute name is DISCOVERED at runtime (first entry of
* Attributes.Retrieve) so the script is portable across tenants; the
* spelling assertions below are the only tenant-specific lines and are
* marked as such.
*
* FIXTURE: ssjs-guide-ts-sub-attrshape / ssjs.guide.ts.sub.attrshape@gmail.com.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function nukeSub(sk) { try { Subscriber.Init(sk).Remove(); } catch (e0) {} }
function countByKey(sk) {
var rows = Subscriber.Retrieve({ Property: "SubscriberKey", SimpleOperator: "equals", Value: sk });
return rows && rows.length ? rows.length : 0;
}
var SK = "ssjs-guide-ts-sub-attrshape";
var EMAIL = "ssjs.guide.ts.sub.attrshape@gmail.com";
nukeSub(SK);
Subscriber.Add({ EmailAddress: EMAIL, SubscriberKey: SK });
/* Read one attribute value by name; "<ABSENT>" when the name is unknown. */
function readAttr(name) {
var a = Subscriber.Init(SK).Attributes.Retrieve();
for (var j = 0; j < a.length; j++) {
if (("" + a[j].Name) === ("" + name)) { return "" + a[j].Value; }
}
return "<ABSENT>";
}
/* Discover a real attribute name on this tenant rather than assuming one. */
var catalog = Subscriber.Init(SK).Attributes.Retrieve();
assert("tenant exposes at least one profile attribute", catalog.length > 0 ? "true" : "false", "true");
var ATTR = "" + catalog[0].Name;
assert("discovered attribute name is a non-empty string", ATTR.length > 0 ? "true" : "false", "true");
assert("a new subscriber starts with that attribute empty", readAttr(ATTR), "");
/* 1. Object form on Upsert — writes. */
var objPayload = { EmailAddress: EMAIL, SubscriberKey: SK, Attributes: {} };
objPayload.Attributes[ATTR] = "shape-obj-upsert";
assert("Upsert with OBJECT Attributes returns \"OK\"", "" + Subscriber.Init(SK).Upsert(objPayload), "OK");
assert("OBJECT Attributes on Upsert DID write", readAttr(ATTR), "shape-obj-upsert");
/* 2. Array form on Upsert — returns OK, writes nothing. */
assert("DEV Upsert with ARRAY Attributes also returns \"OK\" (docs: this is the documented shape)", "" + Subscriber.Init(SK).Upsert({ EmailAddress: EMAIL, SubscriberKey: SK, Attributes: [{ Name: ATTR, Value: "shape-arr-upsert" }] }), "OK");
assert("DEV ARRAY Attributes on Upsert wrote NOTHING (docs: it should set the attribute)", readAttr(ATTR), "shape-obj-upsert");
/* 3. Object form on Update — writes. */
var updPayload = { Attributes: {} };
updPayload.Attributes[ATTR] = "shape-obj-update";
assert("Update with OBJECT Attributes returns \"OK\"", "" + Subscriber.Init(SK).Update(updPayload), "OK");
assert("OBJECT Attributes on Update DID write", readAttr(ATTR), "shape-obj-update");
/* 4. Array form on Update — returns OK, writes nothing. */
assert("DEV Update with ARRAY Attributes also returns \"OK\"", "" + Subscriber.Init(SK).Update({ Attributes: [{ Name: ATTR, Value: "shape-arr-update" }] }), "OK");
assert("DEV ARRAY Attributes on Update wrote NOTHING", readAttr(ATTR), "shape-obj-update");
/* 5. Same asymmetry on the static Add. */
var ADD_ARR_SK = "ssjs-guide-ts-sub-attrshape-addarr";
var ADD_OBJ_SK = "ssjs-guide-ts-sub-attrshape-addobj";
nukeSub(ADD_ARR_SK);
nukeSub(ADD_OBJ_SK);
assert("Add with ARRAY Attributes returns \"OK\"", "" + Subscriber.Add({ EmailAddress: "ssjs.guide.ts.sub.attrshape.addarr@gmail.com", SubscriberKey: ADD_ARR_SK, Attributes: [{ Name: ATTR, Value: "shape-arr-add" }] }), "OK");
var addArrAttrs = Subscriber.Init(ADD_ARR_SK).Attributes.Retrieve();
var addArrValue = "<ABSENT>";
for (var k = 0; k < addArrAttrs.length; k++) {
if (("" + addArrAttrs[k].Name) === ATTR) { addArrValue = "" + addArrAttrs[k].Value; }
}
assert("DEV ARRAY Attributes on Add wrote NOTHING", addArrValue, "");
var addObjPayload = { EmailAddress: "ssjs.guide.ts.sub.attrshape.addobj@gmail.com", SubscriberKey: ADD_OBJ_SK, Attributes: {} };
addObjPayload.Attributes[ATTR] = "shape-obj-add";
assert("Add with OBJECT Attributes returns \"OK\"", "" + Subscriber.Add(addObjPayload), "OK");
var addObjAttrs = Subscriber.Init(ADD_OBJ_SK).Attributes.Retrieve();
var addObjValue = "<ABSENT>";
for (var m = 0; m < addObjAttrs.length; m++) {
if (("" + addObjAttrs[m].Name) === ATTR) { addObjValue = "" + addObjAttrs[m].Value; }
}
assert("OBJECT Attributes on Add DID write", addObjValue, "shape-obj-add");
/* 6. The object form carries more than one attribute per call. */
if (catalog.length > 1) {
var ATTR2 = "" + catalog[1].Name;
var multi = { EmailAddress: EMAIL, SubscriberKey: SK, Attributes: {} };
multi.Attributes[ATTR] = "shape-multi-a";
multi.Attributes[ATTR2] = "shape-multi-b";
assert("Upsert with a multi-key OBJECT returns \"OK\"", "" + Subscriber.Init(SK).Upsert(multi), "OK");
assert("multi-key OBJECT wrote the first attribute", readAttr(ATTR), "shape-multi-a");
assert("multi-key OBJECT wrote the second attribute", readAttr(ATTR2), "shape-multi-b");
}
/* 7. Tenant-specific: attribute names are display names, spaces included. */
assert("TENANT attribute names use the display spelling \"First Name\"", readAttr("First Name") === "<ABSENT>" ? "absent" : "present", "present");
assert("TENANT the docs' \"FirstName\" spelling does not exist", readAttr("FirstName"), "<ABSENT>");
assert("fixture cleanup Remove returns \"OK\"", "" + Subscriber.Init(SK).Remove(), "OK");
assert("cleanup re-count is 0", "" + countByKey(SK), "0");
assert("Add-array fixture cleanup returns \"OK\"", "" + Subscriber.Init(ADD_ARR_SK).Remove(), "OK");
assert("Add-object fixture cleanup returns \"OK\"", "" + Subscriber.Init(ADD_OBJ_SK).Remove(), "OK");
</script>
Notes
For more advanced subscriber management (batch operations, SOAP object access), use WSProxy with the Subscriber SOAP object. See WSProxy.