<WSProxyInstance>.setClientId
→ nullSet the Business Unit client ID for WSProxy operations — allows parent BU scripts to operate on child BU data.
Syntax
<WSProxyInstance>.setClientId(options)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options |
object | Yes | Object with the target ClientId properties; supply the MID under the ID key (and optionally UserID). |
The options object keys:
| Key | Type | Required | Description |
|---|---|---|---|
ID |
number | string | No* | The MID (Member ID) of the target Business Unit. A numeric string targets the same MID as the equivalent number. |
UserID |
number | No | Internal ID of a user to impersonate. Rarely used. |
*At least one of ID / UserID should be supplied.
Show test script
<script runat="server">
/*
* Chapter: Parameters — setClientId(options)
*
* RUN FROM A CHILD BUSINESS UNIT (proven in MCDEV_Training_QA, MID
* 518005426). UNREACHABLE_MID must be a REAL Business Unit of the same
* account that the executing child BU cannot reach — a sibling child BU
* works. The assertions read the requested ClientID back out of the SOAP
* denial, which only appears when the target is genuinely unreachable.
*
* Proves:
* 1. setClientId is a CLR method on every WSProxy instance.
* 2. The documented 1-argument call form works (min_args = 1).
* 3. NEGATIVE — calling with no argument is rejected (min_args = 1).
* 4. NEGATIVE — passing two arguments is rejected (max_args = 1).
* 5. The `ID` key really carries the target MID: after
* setClientId({ ID: UNREACHABLE_MID }) the next retrieve is executed
* against exactly that ClientID (the SOAP Status names it).
* 6. TYPE ACCEPTANCE — the `ID` key accepts a numeric STRING as well as
* a number: setClientId({ ID: "<mid>" }) produces the same
* meaningful result as setClientId({ ID: <mid> }) (identical
* ClientID in the SOAP response), so the key is typed
* `number | string`.
* 7. The optional `UserID` key is accepted as documented (no throw,
* same null return) for both a number and a numeric string.
*
* NOT ASSERTED HERE (NOT ASSERTABLE from this harness): the actual
* impersonation EFFECT of `UserID`. No impersonable user is available to
* the probe, so the key can only be shown to be accepted, not to change
* the executing identity.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertContains(id, actual, fragment) {
var hay = "" + actual;
Platform.Response.Write((hay.indexOf(fragment) >= 0 ? "PASS " : "FAIL ") + id + " -> [" + hay + "]\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");
}
function assertNoThrow(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "FAIL " : "PASS ") + id + " -> " + (threw ? "threw: " + msg : "did not throw") + "\n");
}
/* A REAL sibling child BU of the account — unreachable from this child BU. */
var UNREACHABLE_MID = 7330930;
var proxy = new Script.Util.WSProxy();
/* 1. The method exists on the instance. */
assert("typeof proxy.setClientId is clrmethodinfo", typeof proxy.setClientId, "clrmethodinfo");
/* 2. The documented single-argument form. */
assert("setClientId(options) with one argument is accepted (min_args = 1)", (proxy.setClientId({ ID: UNREACHABLE_MID }) === null) ? "true" : "false", "true");
/* 3. + 4. NEGATIVE — exactly one argument is required. */
assertThrows("setClientId() with no argument throws (min_args = 1)", function () { return proxy.setClientId(); });
assertThrows("setClientId(a, b) with two arguments throws (max_args = 1)", function () { return proxy.setClientId({ ID: UNREACHABLE_MID }, { ID: UNREACHABLE_MID }); });
/* 5. The `ID` key carries the target MID — number form. */
proxy.resetClientIds();
proxy.setClientId({ ID: UNREACHABLE_MID });
var numberCall = proxy.retrieve("DataExtension", ["Name"]);
assertContains("ID as a number targets exactly that MID", numberCall.Status, "ID[" + UNREACHABLE_MID + "]");
/* 6. TYPE ACCEPTANCE — the `ID` key also accepts a numeric string. */
proxy.resetClientIds();
proxy.setClientId({ ID: "" + UNREACHABLE_MID });
var stringCall = proxy.retrieve("DataExtension", ["Name"]);
assertContains("ID as a numeric string targets the same MID (number | string)", stringCall.Status, "ID[" + UNREACHABLE_MID + "]");
/* 7. The optional `UserID` key is accepted. */
proxy.resetClientIds();
assertNoThrow("setClientId({ UserID: <number> }) is accepted", function () { return proxy.setClientId({ UserID: 12345 }); });
proxy.resetClientIds();
assertNoThrow("setClientId({ UserID: <numeric string> }) is accepted", function () { return proxy.setClientId({ UserID: "12345" }); });
/* Always leave the instance in its own-BU context. */
proxy.resetClientIds();
var restored = proxy.retrieve("DataExtension", ["Name"]);
assert("resetClientIds() at the end restores the own BU context", "" + restored.Status, "OK");
</script>
The official docs type setClientId as returning void, but at runtime it returns a genuine null (=== null), not undefined.
Show test script — null return, not void
<script runat="server">
/*
* Differs-from-docs claim: the official Salesforce docs type setClientId
* as returning void, but at runtime it returns a genuine null value, not
* undefined.
*
* Official docs: void (no value)
* SFMC runtime: null (=== null, typeof "object")
*
* Context independent — the CALL is accepted from any Business Unit, so
* the return value can be observed anywhere. TARGET_MID only has to be a
* REAL Business Unit MID; whether it is reachable does not matter here.
*
* Proves both halves of the claim:
* 1. DEV — the returned value strictly equals null (official docs: void,
* which in JS would surface as undefined).
* 2. DEV — the returned value does NOT strictly equal undefined, so a
* caller cannot treat it as a void return.
* 3. DEV — typeof the returned value is "object", the JS typeof of null,
* and not "undefined".
* 4. The distinction is observable through a bound variable: `x == null`
* and `x === null` are true while `x === undefined` is false.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
/* A REAL Business Unit MID of the account. */
var TARGET_MID = 7330930;
var proxy = new Script.Util.WSProxy();
/* 1.-3. The deviation itself. */
assert("DEV setClientId(...) === null (official docs: void)", (proxy.setClientId({ ID: TARGET_MID }) === null) ? "true" : "false", "true");
assert("DEV setClientId(...) === undefined is false (official docs: void)", (proxy.setClientId({ ID: TARGET_MID }) === undefined) ? "true" : "false", "false");
assert("DEV typeof setClientId(...) is object, not undefined (official docs: void)", typeof proxy.setClientId({ ID: TARGET_MID }), "object");
/* 4. The same distinction seen through a bound variable. */
var returned = proxy.setClientId({ ID: TARGET_MID });
assert("DEV the assigned return value is loosely null", (returned == null) ? "true" : "false", "true");
assert("DEV the assigned return value is strictly null", (returned === null) ? "true" : "false", "true");
assert("DEV the assigned return value is not strictly undefined", (returned === undefined) ? "true" : "false", "false");
/* Always leave the instance in its own-BU context. */
proxy.resetClientIds();
var restored = proxy.retrieve("DataExtension", ["Name"]);
assert("resetClientIds() at the end restores the own BU context", "" + restored.Status, "OK");
</script>
Examples
Operate on a child BU from parent
var proxy = new Script.Util.WSProxy();
// Switch context to child BU with MID 123456 (fake)
proxy.setClientId({ ID: 123456 });
// All subsequent operations target the child BU
var result = proxy.retrieve("DataExtension", ["Name", "CustomerKey"]);
var des = result.Results;
Iterate over multiple BUs
var proxy = new Script.Util.WSProxy();
var businessUnits = [
{ name: "US", mid: 123456 },
{ name: "EU", mid: 234567 },
{ name: "APAC", mid: 345678 }
];
for (var i = 0; i < businessUnits.length; i++) {
proxy.setClientId({ ID: businessUnits[i].mid });
var result = proxy.retrieve("DataExtension", ["Name", "CustomerKey"]);
Write(businessUnits[i].name + ": " + result.Results.length + " DEs<br>");
}
Show test script
<script runat="server">
/*
* Chapter: Examples
*
* RUN FROM THE PARENT BUSINESS UNIT of the account (proven in _ParentBU_,
* MID 7281698). Set TARGET_MID_A and TARGET_MID_B to two REAL child BU
* MIDs of that account (Setup -> Account Settings -> Business Units).
* The same script run from a child BU fails every cross-BU assertion —
* see the Notes chapter.
*
* Proves:
* 1. Example "Operate on a child BU from parent" — after
* setClientId({ ID: TARGET_MID_A }) the following
* retrieve("DataExtension", ["Name", "CustomerKey"]) succeeds and the
* returned rows belong to that BU (their Client.ID is TARGET_MID_A,
* not the executing BU's MID).
* 2. All SUBSEQUENT operations target that BU: a second, unrelated
* retrieve on the same instance still returns rows of TARGET_MID_A
* without any further setClientId call.
* 3. Example "Iterate over multiple BUs" — a SECOND setClientId without
* an intervening resetClientIds() replaces the previous target, so
* each loop iteration reads its own BU's rows.
* 4. resetClientIds() restores the executing BU's own context.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertNoThrow(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
Platform.Response.Write((threw ? "FAIL " : "PASS ") + id + " -> " + (threw ? "threw: " + msg : "did not throw") + "\n");
}
/* Two REAL child Business Units of the executing parent's account. */
var TARGET_MID_A = 518005426;
var TARGET_MID_B = 7330930;
var proxy = new Script.Util.WSProxy();
/* Baseline: the script's own BU, before any context switch. */
var before = proxy.retrieve("DataExtension", ["Name", "Client.ID"]);
assert("baseline retrieve in the own BU returns OK", "" + before.Status, "OK");
var ownMid = "" + before.Results[0].Client.ID;
/* 1. Example "Operate on a child BU from parent" — the happy path. */
assertNoThrow("setClientId({ ID: TARGET_MID_A }) is accepted", function () { return proxy.setClientId({ ID: TARGET_MID_A }); });
var result = proxy.retrieve("DataExtension", ["Name", "CustomerKey", "Client.ID"]);
assert("the retrieve after setClientId succeeds", "" + result.Status, "OK");
assert("and its rows belong to the TARGET BU, not the executing one", "" + result.Results[0].Client.ID, "" + TARGET_MID_A);
assert("the target BU's rows are readable as result.Results", (result.Results.length > 0) ? "true" : "false", "true");
/* 2. ALL subsequent operations target that BU. */
var second = proxy.retrieve("DataFolder", ["Name", "Client.ID"]);
assert("a later, unrelated operation still targets that BU", "" + second.Results[0].Client.ID, "" + TARGET_MID_A);
/* 3. Example "Iterate over multiple BUs" — a second setClientId re-targets. */
var businessUnits = [
{ name: "A", mid: TARGET_MID_A },
{ name: "B", mid: TARGET_MID_B }
];
var loopThrew = "false";
var loopMatched = 0;
try {
for (var i = 0; i < businessUnits.length; i++) {
proxy.setClientId({ ID: businessUnits[i].mid });
var loopResult = proxy.retrieve("DataExtension", ["Name", "CustomerKey", "Client.ID"]);
if (("" + loopResult.Status) === "OK" && ("" + loopResult.Results[0].Client.ID) === ("" + businessUnits[i].mid)) {
loopMatched = loopMatched + 1;
}
}
} catch (loopEx) {
loopThrew = "true";
}
assert("the documented multi-BU loop runs without throwing", loopThrew, "false");
assert("every iteration read the rows of ITS OWN target MID (a second setClientId re-targets)", "" + loopMatched, "" + businessUnits.length);
/* 4. Always leave the instance in its own-BU context. */
proxy.resetClientIds();
var restored = proxy.retrieve("DataExtension", ["Name", "Client.ID"]);
assert("resetClientIds() at the end restores the own BU context", "" + restored.Status, "OK");
assert("and the restored rows come from the script's own BU", "" + restored.Results[0].Client.ID, ownMid);
</script>
Return Value
null — the runtime returns a genuine null (proven === null), even though the official docs describe it as void.
Show test script
<script runat="server">
/*
* Chapter: Return Value — `null`
*
* RUN FROM A CHILD BUSINESS UNIT (proven in MCDEV_Training_QA, MID
* 518005426). UNREACHABLE_MID must be a REAL Business Unit the executing
* BU cannot reach — a sibling child BU works.
*
* Proves:
* 1. setClientId() returns a genuine null: `=== null` is true.
* 2. It is NOT undefined: `=== undefined` is false.
* 3. typeof the returned value is "object" — the JS typeof of null,
* not "undefined".
* 4. The return value is not a status signal: the very same null comes
* back for a REAL Business Unit this context cannot reach, so a
* caller cannot use the return value to detect success.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
/* A REAL sibling child BU of the account — unreachable from this child BU. */
var UNREACHABLE_MID = 7330930;
var proxy = new Script.Util.WSProxy();
/* 1.-3. The documented return value. */
assert("setClientId({ ID: ... }) === null", (proxy.setClientId({ ID: UNREACHABLE_MID }) === null) ? "true" : "false", "true");
assert("setClientId({ ID: ... }) !== undefined", (proxy.setClientId({ ID: UNREACHABLE_MID }) === undefined) ? "true" : "false", "false");
assert("typeof setClientId({ ID: ... }) is object (the typeof of null)", typeof proxy.setClientId({ ID: UNREACHABLE_MID }), "object");
/* 4. The same null for an unreachable REAL MID — not a status signal. */
proxy.resetClientIds();
var returned = proxy.setClientId({ ID: UNREACHABLE_MID });
assert("the same null is returned for an UNREACHABLE real MID", (returned === null) ? "true" : "false", "true");
var denied = proxy.retrieve("DataExtension", ["Name"]);
assert("yet the following retrieve is NOT OK, so null was no success signal", ("" + denied.Status === "OK") ? "true" : "false", "false");
/* Always leave the instance in its own-BU context. */
proxy.resetClientIds();
var restored = proxy.retrieve("DataExtension", ["Name"]);
assert("resetClientIds() at the end restores the own BU context", "" + restored.Status, "OK");
</script>
Notes
setClientId itself is accepted from any Business Unit and always returns null — the call is never rejected. Whether the subsequent operation succeeds depends on the executing Business Unit: from a parent Business Unit, operations against the account’s other Business Units succeed; from a child Business Unit, the only MID that works is its own. A child BU cannot reach its parent, and it cannot reach a sibling child BU either — both were runtime-tested with real MIDs and both are denied. Every denial returns a SOAP status naming the executing MID and the requested one (MemberID <executing> does not have access to ClientID: ID[<target>]). So cross-BU work with setClientId has to run from a parent Business Unit.
Find the MID for a BU in: Setup → Account Settings → Business Units → (select BU) → MID column.
Show test script
<script runat="server">
/*
* Chapter: Notes
*
* RUN FROM A CHILD BUSINESS UNIT (proven in MCDEV_Training_QA, MID
* 518005426). This is the chapter that shows the direction restriction,
* so it deliberately runs in the context that is restricted.
*
* Every MID below is a REAL Business Unit of the same account. That is
* essential: a made-up MID cannot distinguish "no such Business Unit"
* from "this Business Unit cannot be reached from here".
*
* PARENT_MID — the parent Business Unit of the executing child BU
* SIBLING_MID — a different child Business Unit of the same parent
*
* Proves:
* 1. The setClientId CALL itself is never rejected — it is accepted and
* returns null for every target, reachable or not.
* 2. Targeting the executing BU's OWN MID: the following operation
* succeeds and returns that BU's rows.
* 3. Targeting the PARENT Business Unit from a child is denied, and the
* SOAP status names the executing MemberID and the requested
* ClientID.
* 4. Targeting a SIBLING child Business Unit is denied the same way —
* a child BU cannot reach sideways either.
* 5. The denial is not object-type specific — a different SOAP object
* is denied identically.
* 6. resetClientIds() restores the executing BU's own context.
*
* NOT ASSERTED (never observable from a CloudPage probe): WHY the backend
* denies these targets. The probe proves what failed, not why.
*
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertContains(id, actual, fragment) {
var hay = "" + actual;
Platform.Response.Write((hay.indexOf(fragment) >= 0 ? "PASS " : "FAIL ") + id + " -> [" + hay + "]\n");
}
/* REAL Business Units of the account the executing child BU belongs to. */
var PARENT_MID = 7281698;
var SIBLING_MID = 7330930;
var proxy = new Script.Util.WSProxy();
/* The executing BU's own MID. */
var own = proxy.retrieve("DataExtension", ["Name", "Client.ID"]);
assert("the executing BU can read its own data", "" + own.Status, "OK");
var ownMid = "" + own.Results[0].Client.ID;
/* 1. + 2. The executing BU's OWN MID: call accepted, operation succeeds. */
assert("setClientId returns null when targeting the OWN MID", (proxy.setClientId({ ID: ownMid }) === null) ? "true" : "false", "true");
var self = proxy.retrieve("DataExtension", ["Name", "Client.ID"]);
assert("targeting the executing BU's OWN MID is allowed", "" + self.Status, "OK");
assert("and returns that BU's rows", "" + self.Results[0].Client.ID, ownMid);
/* 1. + 3. The PARENT Business Unit: call still accepted, operation denied. */
proxy.resetClientIds();
assert("setClientId returns null for the PARENT MID too - the CALL is never rejected", (proxy.setClientId({ ID: PARENT_MID }) === null) ? "true" : "false", "true");
var deniedParent = proxy.retrieve("DataExtension", ["Name"]);
assert("a child BU may NOT operate on its PARENT Business Unit", ("" + deniedParent.Status === "OK") ? "true" : "false", "false");
assertContains("the denial names the executing MemberID", deniedParent.Status, ownMid);
assertContains("the denial names the requested ClientID", deniedParent.Status, "ID[" + PARENT_MID + "]");
/* 1. + 4. A SIBLING child Business Unit: denied the same way. */
proxy.resetClientIds();
assert("setClientId returns null for a SIBLING child MID too", (proxy.setClientId({ ID: SIBLING_MID }) === null) ? "true" : "false", "true");
var deniedSibling = proxy.retrieve("DataExtension", ["Name"]);
assert("a child BU may NOT operate on a SIBLING child Business Unit", ("" + deniedSibling.Status === "OK") ? "true" : "false", "false");
assertContains("the sibling denial names the executing MemberID", deniedSibling.Status, ownMid);
assertContains("the sibling denial names the requested ClientID", deniedSibling.Status, "ID[" + SIBLING_MID + "]");
/* 5. The restriction is not object-type specific. */
var deniedFolder = proxy.retrieve("DataFolder", ["Name"]);
assertContains("a different SOAP object type is denied the same way", deniedFolder.Status, "ID[" + SIBLING_MID + "]");
/* 6. Always leave the instance in its own-BU context. */
proxy.resetClientIds();
var restored = proxy.retrieve("DataExtension", ["Name", "Client.ID"]);
assert("resetClientIds() at the end restores the own BU context", "" + restored.Status, "OK");
assert("and the restored rows come from the executing BU", "" + restored.Results[0].Client.ID, ownMid);
</script>