[SalesForce] Using AMPscript/SSJS how do you unsubscribe a subscriber from a child business unit

My goal is to create a page that passes a business unit id and a subscriber key. The page sits on the parent/enterprise business unit, it will receive business unit ids from a child business unit. Upon clicking submit the page performs a master unsubscribe for that subscriber at the business unit level, but does not perform an enterprise level global unsubscribe.

I've gone through a few iterations.

My first attempt I landed on something like this:

<script runat="server">
  Platform.Load("Core","1.1.1");

  try {

    var isSubmitted = Request.GetFormField("submitted") === "submitted";
    var subscriberKey = Request.GetQueryStringParameter("skey");
    var publicationListId = Request.GetQueryStringParameter("pid");
    var businessUnitId = Request.GetQueryStringParameter("mid");

    var subscriber = Subscriber.Init(subscriberKey);
    var subscriberDEs = DataExtension.Init("ent._subscribers").Rows.Lookup(["SubscriberKey"], [subscriberKey]);

    if (subscriberDEs[0] !== undefined) {
      var subscriberEmail = subscriberDEs[0]["EmailAddress"];

      var hasSubscribedByListId = {};

      if (isSubmitted) {

        if (businessUnitId === "012345678" ) {
          var status = subscriber.Unsubscribe();
        } else {
          Redirect("https://urlForChildBusinessUnit.com/redirectPage?skey=" + subscriberKey, true);
        }

    } else {

      // no subscriber found
      Write("<br /><div class=\"tab-content\"><b>Your subscription was not found.</b></div><br />");

    }

  } catch(ex) {

    Write("An error has occurred: " + Stringify(ex) + "<br />");

  }
</script>

That solution would necessitate each child business unit having something like this to redirect to and make the business unit master unsubscribe:

<script runat="server">
  Platform.Load("Core","1.1.1");

  try {

    var subscriberKey = Request.GetQueryStringParameter("skey");
    var subscriber = Subscriber.Init(subscriberKey);
    var status = subscriber.Unsubscribe();

    // redirect back to a pretty unsubscribe page
    Redirect("http://parentBusinessUnit.com/unsubscribeconfirm", true);

  } catch(ex) {
    Write("An error has occurred: " + Stringify(ex) + "<br />");
  }
</script>

That works fine, however, it feels a little hacky using a redirect like that. Plus, if another child business unit is added in the future then you'd be revisiting this code.

With that in mind, I tried to see if I could come up with a better solution by incorporating AMPscript. I was able to update a subscription to a publication list on a child business unit, so I tried updating the all-subscribers list for a business unit like this:

%%[

  var @publicationListId

  SET @subscriberKey = RequestParameter('skey')
  SET @businessUnitId = RequestParameter('mid')
  SET @submit = RequestParameter('submitted')
  SET @radio = RequestParameter('unsubscribe')

  SET @subscriber = CreateObject('Subscriber')
  SetObjectProperty(@subscriber,'EmailAddress', @subscriberKey)
  SetObjectProperty(@subscriber,'SubscriberKey', @subscriberKey)

  SET @client = CreateObject("ClientID")
  SetObjectProperty(@client, "ID", @businessUnitId)
  SetObjectProperty(@client, "IDSpecified", "true")
  SetObjectProperty(@subscriber, "Client", @client)

  IF @submit == 'submitted'

    // set of ifs to find the all-subscribers list or find a clever way to retrieve it
    IF @businessUnitId == '123456789' THEN
      SET @publicationListId = 456
    ENDIF

    SET @list = CreateObject("SubscriberList")
    SetObjectProperty(@list, "ID", @publicationListId)
    SetObjectProperty(@list, "Client", @client)
    SetObjectProperty(@list, "Status", 'Unsubscribed'))
    AddObjectArrayItem(@subscriber,"Lists", @list)

    SET @update_list_sub = InvokeUpdate(@subscriber, @update_sub_status, @update_sub_errorcode)

  ENDIF

]%%

Despite the status returning that the update was successful, the user is still subscribed.

My next attempt I used code borrowed from a post at sprignaturemoves.com post that detailed injecting an unsub event into the LogUnsubEvent platform table.

%%[

SET @submit = RequestParameter('submitted')
SET @subscriberKey = RequestParameter('skey')
SET @businessUnitId = RequestParameter('mid')
SET @reason = "Business Unit Unsubscribe"

IF @submit == 'submitted' THEN

   var @lue
   var @lue_prop
   var @lue_statusCode
   var @overallStatus

   /* create a request to inject an unsub event into the LogUnsubEvent platform table */
   SET @lue = CreateObject("ExecuteRequest")
   SETObjectProperty(@lue,"Name","LogUnsubEvent")

   /* define and associate Subscriber Key to the request */
   SET @lue_prop = CreateObject("APIProperty")
   SETObjectProperty(@lue_prop, "Name", "SubscriberKey")
   SETObjectProperty(@lue_prop, "Value", @subscriberKey)
   AddObjectArrayItem(@lue, "Parameters", @lue_prop)

   /* define and associate unsub reason to the request */
   SET @lue_prop = CreateObject("APIProperty")
   SETObjectProperty(@lue_prop, "Name", "Reason")
   SETObjectProperty(@lue_prop, "Value", @reason)
   AddObjectArrayItem(@lue, "Parameters", @lue_prop)

   /* finally, you invoke the request */
   SET @lue_statusCode = InvokeExecute(@lue, @overallStatus, @requestId)

ENDIF

]%%

That worked the same way SSJS's subscriber.Unsubscribe() works, performing a master unsubscribe for the business unit that the page resides in or a global unsubscribe if the page belongs to the enterprise/parent business unit. Perhaps it's possible to include a client id into that LogUnsubEvent? I tried to add client id the same way subscriber key is added, but it appears to have no effect.

I feel like I'm either really close and missing something obvious -or- there isn't a way to use SSJS/AMPscript to perform a master unsubscribe from a business unit on a single page. Any help would be appreciated! Thanks!

Best Answer

You can likely handle this by using the ClientID (MID) option in your SOAP API calls. This allows you to impersonate into another BU inside your call. As a note, this is permission based, but as you are coming down from the parent to a child, there should be no issues.

Example using WSProxy:

<script runat="server">
try {
    var prox = new Script.Util.WSProxy();

    /* Set ClientID */
    prox.setClientId({ "ID": mid}); //Impersonates the BU

    var props = [
       { Name: "SubscriberKey", Value: "sample@sample.com" },
       { Name: "EmailAddress", Value: "sample@sample.com" },
       { Name: "JobID", Value: 18099 },
       { Name: "ListID", Value: 8675309 },
       { Name: "BatchID", Value: 0 }
    ];
    var data = prox.execute(props, "LogUnsubEvent");

    Platform.Response.Write(Platform.Function.Stringify(data));
} catch(e) {
    Platform.Response.Write(Platform.Function.Stringify(e));
}
</script>

This is also available in raw soap envelopes and likely also can be used in the AMPscript/SSJS SOAP calls (although I never directly explored this before).

Sample of ClientID in raw SOAP for Perform method:

<PerformRequestMsg xmlns="http://exacttarget.com/wsdl/partnerAPI">
     <Action>start</Action>
     <Definitions>
        <Definition xsi:type="Automation">
           <Client>
              <ID>1234567</ID>
           </Client>
           <ObjectID>d3f5e492-f1a9-47be-a510-15b0d8de65d3</ObjectID>
        </Definition>
     </Definitions>
  </PerformRequestMsg>
Related Topic