2

What I would like to achieve is an email unsubscribe process that is secure, performant and on an external page with the ability to update the internal system table (dataview) _Unsubscribe.

How I imagined this to work is like this:

  1. Create an AMPScript variable inside a newsletter that stores the values that are needed for the _Unsubscribe dataview separated by a special character
  2. Encrypt the variable with AES
  3. Create an Unsubscribe Link in the e-mail with a typical a href to our external page and and concat with encrypted variable on it as a parameter value. First three points shown here?
  4. Decrypt the url-parameter with the backend of our external shopengine and store it in different variables (RegEx on special sign)
  5. Use the client-id and secret from a package created in the marketing cloud
  6. Retrieve the access token
  7. Do the last two steps with Java Axis 1.4 or something else
  8. Create a SOAP-API Call and insert the values into it
  9. Send this data to the right endpoint (which is?) when the user clicks the unsubscribe button in the website

Here my Questions:

  • Is this the right way to do it?
  • Is there a simpler way when you are using an external page and not cloudpages?
  • Is the point 2 secure, what do i have to make sure that point 2 is secure?
  • Are there any documents you know that can help me with this task?

Kind regards for reading

2
  • Is this a one click unsub or would this be a click that takes them to a form to unsub? Commented Dec 4, 2018 at 14:31
  • 1
    Salesforce does not support 3rd party encryption, and support has told me that their encryption methods include a private salt that is unique to each stack, which means using traditional means to encrypt/decrypt AES/DES will fail. I've attempted this and found that to be the case, at least on S1. I was thinking about a workaround to this recently, and here's my thoughts: You'd need to AES Encrypt the SubscriberKey in Salesforce and then post the encrypted value to a data extension which you then retrieve by asking it to decrypt that field in the API call on the Marketing Cloud side Commented Dec 4, 2018 at 18:48

1 Answer 1

9

Logging an unsub event is the way to get unsubscribes logged properly (and ultimately in the _Unsubscribe data view). You can't insert into directly into any of the data views.

You'd need jobID, listID, _JobSubscriberBatchID and an encrypted or obfuscated _subscriberkey to replicate what's done on a one-click unsubscribe page. You may be able to pass and use SubscriberID to get around the encryption part, but I haven't tested the API call with that value.

Here's the code in AMPscript. The SOAP call would use these same objects and methods:

%%[

var @debug
var @jid
var @listid
var @batchid
var @email
var @skey
var @reason
var @unsubscribeAll

set @debug = 0
set @jid = AttributeValue("jobid")
set @listid = AttributeValue("listid")
set @batchid = AttributeValue("_JobSubscriberBatchID")
set @email = AttributeValue("emailaddr")
set @skey = AttributeValue("_subscriberkey")
set @reason = "One-Click Unsubscribe"
set @unsubscribeAll = RequestParameter("ua")

/* if we know the subscriber */
if not empty(@skey) then

   var @lue
   var @lue_prop
   var @lue_statusCode
   var @overallStatus
   var @requestId
   var @Response
   var @Status
   var @Error

   /* if unsubscribing from all, then set the job, batch and listids to blank, effectively doing a global unsub */
   if @unsubscribeAll == "1" then
     set @jid = ""
     set @listid = ""
     set @batchid = ""
   endif

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

   /*
    In order to invoke the request, we need to associate the following information with it to define the subscriber context and the job context:

    1. Subscriber Key
    2. JobId associated with the email send
    3. ListID the email was sent to
    4. BatchID the email was sent to
    5. Reason for the unsub
   */

   /* 1. define and associate Subscriber Key to the request */
   set @lue_prop = CreateObject("APIProperty")
   SetObjectProperty(@lue_prop, "Name", "SubscriberKey")
   SetObjectProperty(@lue_prop, "Value", @skey)
   AddObjectArrayItem(@lue, "Parameters", @lue_prop)

   /* 2. define and associate JobID to the request */
   if not empty(@jid) then
    set @lue_prop = CreateObject("APIProperty")
    SetObjectProperty(@lue_prop, "Name", "JobID")
    SetObjectProperty(@lue_prop, "Value", @jid)
    AddObjectArrayItem(@lue, "Parameters", @lue_prop)
   endif

   /* 3. define and associate ListID to the request */
   if not empty(@listid) then
     set @lue_prop = CreateObject("APIProperty")
     SetObjectProperty(@lue_prop, "Name", "ListID")
     SetObjectProperty(@lue_prop, "Value", @listid)
     AddObjectArrayItem(@lue, "Parameters", @lue_prop)
   endif

   /* 4. define and associate BatchID to the request */
   if not empty(@batchid) then
    set @lue_prop = CreateObject("APIProperty")
    SetObjectProperty(@lue_prop, "Name", "BatchID")
    SetObjectProperty(@lue_prop, "Value", @batchid)
    AddObjectArrayItem(@lue, "Parameters", @lue_prop)
   endif

   /* 5. 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)

   /* extract messages from the response */
   set @Response = Row(@lue_statusCode, 1)
   set @Status = Field(@Response,"StatusMessage")
   set @Error = Field(@Response,"ErrorCode")

endif

if @debug == 1 then
  output(concat("<br>jid: ", @jid))
  output(concat("<br>listid: ", @listid))
  output(concat("<br>batchid: ", @batchid))
  output(concat("<br>email: ", @email))
  output(concat("<br>skey: ", @skey))
  output(concat("<br>reason: ", @reason))
  output(concat("<br>unsubscribeAll: ", @unsubscribeAll))
  output(concat("<br>overallStatus: ", @overallStatus))
  output(concat("<br>requestId: ", @requestId))
  output(concat("<br>Response: ", @Response))
  output(concat("<br>Status: ", @Status))
  output(concat("<br>Error: ", @Error))
endif

]%%

Reference

5
  • I tested this and I experienced a behaviour which I do not fully understand. When you do and invoke call with with ua = 0 the values get written to the dataview _Unsubscribe, when ua = 1 it does not get written to that data_view. When you try to do a ua=0 call and after that a ua=1 call the ua=1 call will error. Is this supposed to be that way? Where can global unsubscribes be found (dataviews etc)? What is the difference between unsubscribing from the all_subscribers list and an global unsubscribe? Commented Feb 7, 2019 at 10:11
  • 1
    If the system can't tie the unsub back to a particular send job then it's a global unsubscribe. Details are in the link I added to my answer. Commented Apr 17, 2019 at 13:34
  • Hi Adam, i recently stumbled upon this code again and i was asking myself why you attach "reason" to the UnsubEvent. As far as i can see there is no field called reason in the dataview unsubscribe. Or is there a field called reason which salesforce forgot to mention? Commented May 23, 2019 at 14:14
  • 1
    It's not in any of the data views, but it is associated with the subscriber in All Subscribers. Logging an unsub event allows you to set a custom message. Commented May 23, 2019 at 16:25
  • The UnsubReason is available via Tracking Extract, in the Unsub file. It's not documented that I can find. We often extract and import this data (along with NotSent) into Data Extensions. Commented Jul 11, 2019 at 15:20

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .