Syntax

Platform.Function.InvokeRetrieve(apiObject, status)
2 arguments
Show test script — array vs null return, the error/no-match distinction and the pre-sized-vs-empty status control
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Callout: differs-from-docs — the return value, the null ambiguity and the
 * status out-parameter.
 *
 * Proves:
 *   1. On a retrieve that MATCHES rows the call returns an ARRAY of result
 *      objects, and each element exposes a Properties collection of
 *      Name / Value pairs.
 *   2. DEVIATION — on a retrieve that matches NO rows the call returns a
 *      genuine null (not an empty array, not undefined). The official docs
 *      type the return value as an object array only and never mention the
 *      null return.
 *   3. DEVIATION — on a retrieve that ERRORS the call ALSO returns null, so
 *      the return value alone cannot distinguish "no rows" from "failed".
 *   4. The discriminator is status[0]: it reads "OK" on BOTH success paths
 *      (rows and no rows) and starts with "Error: " when the retrieve
 *      failed. This is the opposite defect shape to
 *      /platform-functions/invokeperform/ and
 *      /platform-functions/invokecreate/, where status[0] holds a status
 *      MESSAGE and the RETURN value is what equals "OK". Never carry a
 *      status-slot conclusion across verbs.
 *   5. PAIRED CONTROL on the status out parameter — three otherwise
 *      IDENTICAL, SUCCESSFUL retrieves differing only in the initial size
 *      of the status array. A PRE-SIZED [0, 0] IS populated in both slots;
 *      an EMPTY [] is never grown and stays at length 0; a [0, 0, 0] keeps
 *      its third slot at its initial value because only two slots are ever
 *      written. The engine writes only into slots the array ALREADY has.
 *      This is the InvokeExecute / InvokePerform behaviour
 *      (/platform-functions/invokeexecute/, /platform-functions/invokeperform/)
 *      and NOT the InvokeExtract one (/platform-functions/invokeextract/),
 *      where even a pre-sized array stays untouched because the call throws
 *      first. An observation that status.length stays 0 is MEANINGLESS
 *      unless the array was pre-sized.
 *   6. status[1] receives a RequestID GUID STRING of the canonical
 *      36-character length — the InvokeExecute slot shape, NOT the
 *      InvokePerform one where status[1] is a NUMBER.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit). The page also
 * lists automation availability; the automation context was not exercised.
 *
 * EXPECTED OUTPUT: every line starts with PASS. A FAIL means the runtime no
 * longer matches the documented claim and the page must be revised.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}

var prox = new Script.Util.WSProxy();
var runId = String(Platform.Function.Now().getTime());
var deKey = "ssjsg_ir_diff_de_" + runId;

/* Setup — a throwaway Data Extension with one known row. */
var deRes = prox.createItem("DataExtension", {
    CustomerKey: deKey, Name: deKey,
    Fields: [{ Name: "SubscriberKey", FieldType: "Text", MaxLength: 50, IsPrimaryKey: true, IsRequired: true }]
});
assert("setup: the throwaway Data Extension is created", String(deRes.Status), "OK");
Platform.Function.InsertData(deKey, ["SubscriberKey"], ["row1"]);

/* A RetrieveRequest against that Data Extension, filtered on one value. */
function req(value) {
    var r = Platform.Function.CreateObject("RetrieveRequest");
    Platform.Function.SetObjectProperty(r, "ObjectType", "DataExtensionObject[" + deKey + "]");
    Platform.Function.AddObjectArrayItem(r, "Properties", "SubscriberKey");
    var f = Platform.Function.CreateObject("SimpleFilterPart");
    Platform.Function.SetObjectProperty(f, "Property", "SubscriberKey");
    Platform.Function.SetObjectProperty(f, "SimpleOperator", "equals");
    Platform.Function.AddObjectArrayItem(f, "Value", value);
    Platform.Function.SetObjectProperty(r, "Filter", f);
    return r;
}

/* 1 + 5 + 6. The documented call, PRE-SIZED [0, 0], matching one row. */
var sized = [0, 0];
var hits = Platform.Function.InvokeRetrieve(req("row1"), sized);
assert("a retrieve that matches rows returns an object, not a string", String(typeof hits), "object");
assert("a retrieve that matches rows does NOT return null", hits === null ? "true" : "false", "false");
assert("the returned array carries one element for the one matching row", hits.length, 1);
assert("each result element exposes its Properties collection", hits[0].Properties.length, 1);
assert("the Properties collection carries the requested column name", String(hits[0].Properties[0].Name), "SubscriberKey");
assert("the Properties collection carries the stored value", String(hits[0].Properties[0].Value), "row1");
assert("a PRE-SIZED status array keeps its length", sized.length, 2);
assert("DEV status[0] IS populated on the success path - it reads 'OK' (the page previously claimed status was inert)", String(sized[0]), "OK");
assert("status[0] is a string", String(typeof sized[0]), "string");
assert("status[1] receives a RequestID GUID STRING (InvokePerform puts a NUMBER here)", String(typeof sized[1]), "string");
assert("the RequestID GUID has the canonical 36-character length", String(sized[1]).length, 36);

/* 5. CONTROL — the SAME successful call with an EMPTY status array. */
var empty = [];
var hitsE = Platform.Function.InvokeRetrieve(req("row1"), empty);
assert("control: the identical call still returns the one matching row", hitsE.length, 1);
assert("control: an EMPTY status array is never grown - it stays at length 0", empty.length, 0);
assert("control: status[0] of an empty array is undefined", empty[0] === undefined ? "true" : "false", "true");
assert("control: status[1] of an empty array is undefined", empty[1] === undefined ? "true" : "false", "true");

/* 5. CONTROL — a 3-slot array: only two slots are ever written. */
var three = [0, 0, 0];
Platform.Function.InvokeRetrieve(req("row1"), three);
assert("control: a 3-slot status array keeps its length", three.length, 3);
assert("control: a 3-slot status array still receives the overall status", String(three[0]), "OK");
assert("control: only two slots are ever written - status[2] keeps its initial value", three[2], 0);

/* 2 + 4. A successful retrieve that matches NO rows. */
var noneStatus = [0, 0];
var none = Platform.Function.InvokeRetrieve(req("nosuchrow"), noneStatus);
assert("DEV a retrieve that matches NO rows returns a genuine null (docs: an object array)", none === null ? "true" : "false", "true");
assert("the no-rows null is not undefined", none === undefined ? "true" : "false", "false");
assert("typeof the no-rows null is object", String(typeof none), "object");
assert("the no-rows retrieve nonetheless SUCCEEDED - status[0] reads 'OK'", String(noneStatus[0]), "OK");
assert("the no-rows retrieve still received a RequestID GUID", String(noneStatus[1]).length, 36);

/* 3 + 4. A retrieve that ERRORS returns null too. */
var errStatus = [0, 0];
var errReq = Platform.Function.CreateObject("RetrieveRequest");
Platform.Function.SetObjectProperty(errReq, "ObjectType", "DataExtensionObject[ssjsg_no_such_de_" + runId + "]");
Platform.Function.AddObjectArrayItem(errReq, "Properties", "SubscriberKey");
var errResult = Platform.Function.InvokeRetrieve(errReq, errStatus);
assert("DEV a retrieve that ERRORS returns null too - the return value cannot tell error from no-rows apart", errResult === null ? "true" : "false", "true");
assert("the failing retrieve does NOT throw", String(typeof errResult), "object");
assert("status[0] IS the discriminator: it starts with 'Error: ' on the failure path", String(errStatus[0]).indexOf("Error: ") === 0 ? "true" : "false", "true");
assert("the failure message names the missing Data Extension", String(errStatus[0]).indexOf("Data extension does not exist") > 0 ? "true" : "false", "true");
assert("the failing retrieve still received a RequestID GUID", String(errStatus[1]).length, 36);

/* Cleanup — the business unit is left clean. */
assert("cleanup: the throwaway Data Extension is deleted again", String(prox.deleteItem("DataExtension", { CustomerKey: deKey }).Status), "OK");
</script>

Parameters

Name Type Required Description
apiObject object Yes Configured Retrieve request object
status array Yes Out-parameter that receives the overall status (status[0]) and the RequestID GUID (status[1]). Pass a pre-sized array — [0, 0]

The status array is a true out parameter, but it is never grown: only the slots it already has are written. Pass [0, 0] — an empty [] stays empty and reads back undefined. Only two slots are ever filled, so a [0, 0, 0] leaves status[2] at its initial value.

A null apiObject does not throw: the call returns null and leaves the status array untouched, because no SOAP call is made. A plain JavaScript object or a string as apiObject throws, and so does a non-array status.

Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Parameters — Platform.Function.InvokeRetrieve(apiObject, status)
 *
 * Proves, one parameter at a time:
 *   1. apiObject is a SOAP object built with CreateObject and configured
 *      with SetObjectProperty / AddObjectArrayItem. Such an object is a
 *      .NET CLR host object (typeof "clr"); SetObjectProperty and
 *      AddObjectArrayItem both return a genuine null, NOT undefined.
 *   2. A plain JavaScript object or a string as apiObject throws — the
 *      parameter is not structurally typed, it must be an
 *      ExactTarget.Integration.WSDL type.
 *   3. A null apiObject is the one non-object that does NOT throw: no SOAP
 *      call is made, the return value is a genuine null (not undefined, not
 *      an array) and the status array is left untouched. This mirrors the
 *      same finding on /platform-functions/invokecreate/,
 *      /platform-functions/invokedelete/, /platform-functions/invokeexecute/,
 *      /platform-functions/invokeextract/, /platform-functions/invokeperform/
 *      and /platform-functions/invokeconfigure/.
 *   4. status must be an array — a non-array throws — and it is a genuine
 *      OUT parameter that must be PRE-SIZED: with [0, 0] both documented
 *      slots are filled (overall status string, RequestID GUID string).
 *   5. ARITY — 2 arguments is the ONLY valid arity. 0, 1 and 3 all throw.
 *      Never assume an options argument is reachable just because a sibling
 *      verb has one: /platform-functions/invokeperform/ accepts 3 or 4.
 *   6. An unresolvable ObjectType is REJECTED BY THE API, not by the
 *      function: the call returns null without throwing and reports the
 *      reason in status[0]. That is an example-payload problem, not a
 *      function failure.
 *
 * NOT ASSERTED: the values set on the apiObject cannot be read back from
 * the object itself — it is a CLR host object and the engine blocks all
 * introspection of it (see /platform-functions/createobject/). They are
 * proven indirectly by the API's own response.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}

/* 1. apiObject is a CreateObject SOAP object — a CLR host object. */
var apiObject = Platform.Function.CreateObject("RetrieveRequest");
assert("typeof CreateObject('RetrieveRequest') is clr", String(typeof apiObject), "clr");
var setResult = Platform.Function.SetObjectProperty(apiObject, "ObjectType", "Email");
assert("SetObjectProperty(apiObject, 'ObjectType', ...) returns a genuine null", setResult === null ? "true" : "false", "true");
assert("SetObjectProperty does NOT return undefined", setResult === undefined ? "true" : "false", "false");
var addResult = Platform.Function.AddObjectArrayItem(apiObject, "Properties", "Email.Name");
assert("AddObjectArrayItem(apiObject, 'Properties', ...) returns a genuine null", addResult === null ? "true" : "false", "true");
assert("AddObjectArrayItem does NOT return undefined", addResult === undefined ? "true" : "false", "false");

function req() {
    var r = Platform.Function.CreateObject("RetrieveRequest");
    Platform.Function.SetObjectProperty(r, "ObjectType", "Email");
    Platform.Function.AddObjectArrayItem(r, "Properties", "Email.Name");
    return r;
}

/* 4. The documented 2-argument call fills both status slots. */
var status = [0, 0];
Platform.Function.InvokeRetrieve(apiObject, status);
assert("status is an OUT parameter: status[0] received the overall status", String(status[0]), "OK");
assert("status is an OUT parameter: status[1] received a RequestID GUID string", String(status[1]).length, 36);
assert("the pre-sized status array keeps its length", status.length, 2);

/* 5. Arity outside 2 throws. */
assertThrows("arity 0 throws (the only valid arity is 2)", function () {
    return Platform.Function.InvokeRetrieve();
});
assertThrows("arity 1 throws (the only valid arity is 2)", function () {
    return Platform.Function.InvokeRetrieve(req());
});
assertThrows("DEV arity 3 throws - there is NO options argument on this verb (InvokePerform accepts 3 or 4)", function () {
    var s = [0, 0];
    return Platform.Function.InvokeRetrieve(req(), s, null);
});

/* 2. apiObject must be a real SOAP object. */
assertThrows("a plain JavaScript object as apiObject throws", function () {
    var s = [0, 0];
    return Platform.Function.InvokeRetrieve({}, s);
});
assertThrows("a string as apiObject throws", function () {
    var s = [0, 0];
    return Platform.Function.InvokeRetrieve("Email", s);
});

/* 3. null apiObject: no SOAP call is made — genuine null back, status untouched. */
var nullStatus = [0, 0];
var nullResult = Platform.Function.InvokeRetrieve(null, nullStatus);
assert("a null apiObject does NOT throw", nullResult === null ? "true" : "false", "true");
assert("a null apiObject returns a genuine null, not an array", String(typeof nullResult), "object");
assert("a null apiObject is not undefined", nullResult === undefined ? "true" : "false", "false");
assert("a null apiObject leaves status[0] untouched (no SOAP call was made)", nullStatus[0], 0);
assert("a null apiObject leaves status[1] untouched (no SOAP call was made)", nullStatus[1], 0);

/* 4. status must be an array. */
assertThrows("a non-array status throws", function () {
    return Platform.Function.InvokeRetrieve(req(), "notanarray");
});

/* 6. An unresolvable ObjectType is rejected by the API, not by the function. */
var badType = Platform.Function.CreateObject("RetrieveRequest");
Platform.Function.SetObjectProperty(badType, "ObjectType", "NotARealSoapType");
Platform.Function.AddObjectArrayItem(badType, "Properties", "Name");
var badStatus = [0, 0];
var badResult = Platform.Function.InvokeRetrieve(badType, badStatus);
assert("an unresolvable ObjectType does NOT throw - it returns null", badResult === null ? "true" : "false", "true");
assert("the API reports the rejected ObjectType in status[0]", String(badStatus[0]), "Error: NotARealSoapType is not a valid ObjectType.");
assert("the rejected-ObjectType call still received a RequestID GUID", String(badStatus[1]).length, 36);
</script>

Examples

var RetrieveRequest = Platform.Function.CreateObject("RetrieveRequest");
Platform.Function.SetObjectProperty(RetrieveRequest, "ObjectType", "Email");
Platform.Function.AddObjectArrayItem(RetrieveRequest, "Properties", "Email.Name");

var filter = Platform.Function.CreateObject("SimpleFilterPart");
Platform.Function.SetObjectProperty(filter, "Property", "Status");
Platform.Function.SetObjectProperty(filter, "SimpleOperator", "equals");
Platform.Function.AddObjectArrayItem(filter, "Value", "Active");
Platform.Function.SetObjectProperty(RetrieveRequest, "Filter", filter);

var StatusAndRequestID = [0, 0];
var Emails = Platform.Function.InvokeRetrieve(RetrieveRequest, StatusAndRequestID);

if (StatusAndRequestID[0] !== "OK") {
    // the retrieve failed - StatusAndRequestID[0] carries the reason
} else if (Emails === null) {
    // the retrieve succeeded but matched no rows
}

A SimpleFilterPart’s Value is a collection: set it with AddObjectArrayItem, not with SetObjectProperty — the latter throws.

Guard on status[0], not on the return value: null means either “no rows” or “the retrieve failed”, and only status[0] separates the two.

WSProxy.retrieve() is simpler and preferred for new code.

Show test script
<script runat="server">
Platform.Load("core", "1.1.5");

/*
 * Chapter: Examples — the CreateObject / SetObjectProperty /
 * AddObjectArrayItem / InvokeRetrieve build-and-retrieve pattern.
 *
 * Proves:
 *   1. Every line of the page's example runs: CreateObject
 *      ("RetrieveRequest"), SetObjectProperty for the ObjectType,
 *      AddObjectArrayItem for the requested column, a SimpleFilterPart
 *      configured with Property / SimpleOperator / Value, and the final
 *      2-argument InvokeRetrieve with a [0, 0] status array. The call
 *      succeeds — status[0] reads "OK".
 *   2. A SimpleFilterPart's Value is a COLLECTION: the example uses
 *      AddObjectArrayItem for it on purpose. SetObjectProperty on "Value"
 *      throws, so the AddObjectArrayItem form is the working invocation.
 *   3. The retrieve really ran — it is NOT a presence-only check.
 *      DISCRIMINATING CONTROL against a throwaway Data Extension the script
 *      creates itself and fills with one known row: the SAME call shape
 *      returns a one-element array for the value that exists and a genuine
 *      null for a value that does not, while status[0] reads "OK" both
 *      times. A call that had silently done nothing could not distinguish
 *      the two.
 *   4. The example's guard is on status[0], not on the return value,
 *      because null means EITHER "no rows" OR "the retrieve failed": on
 *      the no-rows path status[0] !== "OK" is false, on the error path it
 *      is true, and the return value is null in both. This is the OPPOSITE
 *      of /platform-functions/invokeperform/, where the RETURN value is
 *      what equals "OK" and a status[0] guard fires on every successful
 *      call — never carry a guard shape across verbs.
 *   5. The script leaves nothing behind: the throwaway Data Extension is
 *      deleted again.
 *
 * SCOPE: CloudPage only (MCDEV_Training_QA business unit).
 *
 * EXPECTED OUTPUT: every line starts with PASS.
 */

function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}

/* 1. The page's example, line by line. */
var RetrieveRequest = Platform.Function.CreateObject("RetrieveRequest");
assert("example line 1: CreateObject('RetrieveRequest') yields a CLR host object", String(typeof RetrieveRequest), "clr");
assert("example line 2: SetObjectProperty(RetrieveRequest, 'ObjectType', 'Email') returns a genuine null", Platform.Function.SetObjectProperty(RetrieveRequest, "ObjectType", "Email") === null ? "true" : "false", "true");
assert("example line 3: AddObjectArrayItem(RetrieveRequest, 'Properties', 'Email.Name') returns a genuine null", Platform.Function.AddObjectArrayItem(RetrieveRequest, "Properties", "Email.Name") === null ? "true" : "false", "true");

var filter = Platform.Function.CreateObject("SimpleFilterPart");
assert("example line 4: CreateObject('SimpleFilterPart') yields a CLR host object", String(typeof filter), "clr");
assert("example line 5: the filter's Property is a scalar - SetObjectProperty works", Platform.Function.SetObjectProperty(filter, "Property", "Status") === null ? "true" : "false", "true");
assert("example line 6: the filter's SimpleOperator is a scalar - SetObjectProperty works", Platform.Function.SetObjectProperty(filter, "SimpleOperator", "equals") === null ? "true" : "false", "true");
assert("example line 7: the filter's Value is a COLLECTION - AddObjectArrayItem works", Platform.Function.AddObjectArrayItem(filter, "Value", "Active") === null ? "true" : "false", "true");
assert("example line 8: the configured filter is attached to the request", Platform.Function.SetObjectProperty(RetrieveRequest, "Filter", filter) === null ? "true" : "false", "true");

var StatusAndRequestID = [0, 0];
var Emails = Platform.Function.InvokeRetrieve(RetrieveRequest, StatusAndRequestID);
assert("example line 9: the retrieve SUCCEEDS - status[0] reads 'OK'", String(StatusAndRequestID[0]), "OK");
assert("example line 9: the retrieve received a RequestID GUID", String(StatusAndRequestID[1]).length, 36);

/* 2. SetObjectProperty is NOT the way to set a collection-valued Value. */
var badFilter = Platform.Function.CreateObject("SimpleFilterPart");
Platform.Function.SetObjectProperty(badFilter, "Property", "Status");
Platform.Function.SetObjectProperty(badFilter, "SimpleOperator", "equals");
assertThrows("SetObjectProperty(filter, 'Value', ...) throws - Value is a collection, use AddObjectArrayItem", function () {
    return Platform.Function.SetObjectProperty(badFilter, "Value", "Active");
});

/* 3 + 4. DISCRIMINATING CONTROL on a Data Extension the script owns. */
var prox = new Script.Util.WSProxy();
var runId = String(Platform.Function.Now().getTime());
var deKey = "ssjsg_ir_ex_de_" + runId;
assert("setup: the throwaway Data Extension is created", String(prox.createItem("DataExtension", {
    CustomerKey: deKey, Name: deKey,
    Fields: [{ Name: "SubscriberKey", FieldType: "Text", MaxLength: 50, IsPrimaryKey: true, IsRequired: true }]
}).Status), "OK");
Platform.Function.InsertData(deKey, ["SubscriberKey"], ["row1"]);

function deReq(value) {
    var r = Platform.Function.CreateObject("RetrieveRequest");
    Platform.Function.SetObjectProperty(r, "ObjectType", "DataExtensionObject[" + deKey + "]");
    Platform.Function.AddObjectArrayItem(r, "Properties", "SubscriberKey");
    var f = Platform.Function.CreateObject("SimpleFilterPart");
    Platform.Function.SetObjectProperty(f, "Property", "SubscriberKey");
    Platform.Function.SetObjectProperty(f, "SimpleOperator", "equals");
    Platform.Function.AddObjectArrayItem(f, "Value", value);
    Platform.Function.SetObjectProperty(r, "Filter", f);
    return r;
}

var hitStatus = [0, 0];
var hits = Platform.Function.InvokeRetrieve(deReq("row1"), hitStatus);
assert("control: the value that EXISTS comes back as a one-element array", hits.length, 1);
assert("control: the row carries the value the script stored", String(hits[0].Properties[0].Value), "row1");
assert("control: the matching retrieve reports status[0] 'OK'", String(hitStatus[0]), "OK");

var missStatus = [0, 0];
var miss = Platform.Function.InvokeRetrieve(deReq("nosuchrow"), missStatus);
assert("control: the value that does NOT exist comes back as a genuine null", miss === null ? "true" : "false", "true");
assert("control: the no-rows retrieve ALSO reports status[0] 'OK' - it did not fail", String(missStatus[0]), "OK");

var failStatus = [0, 0];
var failReq = Platform.Function.CreateObject("RetrieveRequest");
Platform.Function.SetObjectProperty(failReq, "ObjectType", "DataExtensionObject[ssjsg_no_such_de_" + runId + "]");
Platform.Function.AddObjectArrayItem(failReq, "Properties", "SubscriberKey");
var failResult = Platform.Function.InvokeRetrieve(failReq, failStatus);
assert("control: a FAILING retrieve returns null too - identical to the no-rows return value", failResult === null ? "true" : "false", "true");

/* 4. The example's guard separates the two null cases; a return-value guard cannot. */
assert("the example guard status[0] !== 'OK' does NOT fire on the no-rows path", missStatus[0] !== "OK" ? "true" : "false", "false");
assert("the example guard status[0] !== 'OK' DOES fire on the failure path", failStatus[0] !== "OK" ? "true" : "false", "true");
assert("a return-value guard could not tell the two apart - both returned null", (miss === null && failResult === null) ? "true" : "false", "true");

/* 5. Cleanup — the business unit is left clean. */
assert("cleanup: the throwaway Data Extension is deleted again", String(prox.deleteItem("DataExtension", { CustomerKey: deKey }).Status), "OK");
</script>

See Also