Matt Brocious - Subscriber Preferences Management
Published on 06/06/2020
For the “preferences management” solution, I wanted an option that was user-friendly (both internal and external users), that was scalable for a business of most any size, and that played to the strengths of both the Marketing Cloud tool and the AMPscript coding language.
As a quick overview, this solution uses a CloudPage to capture user’s email content preferences and if they want to unsubscribe from your emails or not. Once submitted, the preferences and whether or not the user is mailable (unsubscribed or active) is stored in a data extension which can be used to segment customers or can be used to populate a field in your customer master. Additionally, we use a data extension that holds your preferences to populate the form on the CloudPage. This will allow you to easily modify your preferences without making major updates to the form.
Before I begin describing the setup in more detail, there are a few things you’ll want to make sure are setup within your account:
This setup also makes a few assumptions, so if they don’t apply to your business then you might need to make some adjustments:
So, if you have everything setup and are okay with how this solution works, let’s start building.
For this setup, you will be creating two data extensions. One to house the user's preferences and one to house all of the preferences we are listing out on the page.
First, you'll want to build the preference center data extension. For this setup, we are creating the data extension within the Shared Data Extensions folder via the parent business unit. Below is the data extension we are using for this setup but make any necessary adjustments for your use case.
Next, you'll want to set up a data extension that will be populated by the preferences you want listed out in your preference center page. This will allow you to easily add additional preferences as your marketing efforts grow more complex. The "priority" field will be used to denote the order in which the preferences should be displayed. The "key" field will be the same for all preferences and will be used in the AMPscript within the CloudPage.
Finally, if necessary, add any additional fields to your customer master you'd like to use in conjunction the prewithference center. For example, a “Preferences” field.
Now that the data extensions are setup, we’ll move onto the CloudPages. For our setup we'll need one CloudPage that will house the form and process the user's data and one page to be used as an error page.
So, in the CloudPages section of Marketing Cloud, let’s create a new collection named “Email Preference Center”.
Next, create a new page named “Error”. This page doesn’t need much but feel free to stylize and needed for your use case. For this example, we’re simply going to have some text on the page that reads “An error has occurred. Try your request again or contact your system administrator.”.
Once that is done, we’ll move on to creating our main preferences page. Let’s create a new landing page named "Email Preference Center". You can design the form as you wish, but you will need the elements listed below on your page for this setup. For an example page - see document sfmc_prefcenter.html.
The below AMPscript code should be added to the top of your page and is used to update the user's preferences and status on the master preference center data extension and, if the user unsubscribes, within the All Subscribers. It also sets a confirmation message which is displayed on the page after a user takes an action. If the page is accessed and there is no valid email address provided, the page will redirect to the error page we created in the previous step. There are notes in the code to help show which lines are performing which actions.
And, if you aren’t familiar with some of the AMPscript functions being used, it might be helpful to read up on them. Here are some of the functions being used:
IsEmailAddress – https://ampscript.guide/isemailaddress/
RequestParameter – https://ampscript.guide/requestparameter/
Field - https://ampscript.guide/field/
Create Object - https://ampscript.guide/CreateObject
SetObjectProperty - https://ampscript.guide/SetObjectProperty
AddObjectArrayItem - https://ampscript.guide/AddObjectArrayItem
InvokeExecute - https://ampscript.guide/invokeexecute
Empty - https://ampscript.guide/empty/
%%[ /* Set the variables used in this page */ var @email, @jobid, @submitted, @unsubscribe, @preference, @mod_date, @url set @email = emailaddr set @jobid = jobid set @submitted = requestparameter("submitted") set @unsubscribe = requestparameter("unsubscribe") set @preference = requestparameter("preference") set @mod_date = Now() /* If the @email variable is empty but the @submitted or @unsubscribe value are populated with the correct values, populate the @email variable with the email parameter. */ if empty(@email) and (@submitted == "Yes" or @unsubscribe == "Yes") then set @email = requestparameter("email") else endif /* If there is no valid email address provided, then redirect the user to the error page */ if isemailaddress(@email) == "false" then set @url = "errorpagelinkhere.com" redirect(@url) else endif /* If the user submitted their preferences, then upsert the record on the master preference center data extension and update their status to active */ if @submitted == "Yes" then UpsertDE("Master_PreferenceCenter_CustomerPreferences",1,"Email_Address",@email,"Mailable","Y","Preferences",@preference,"Last_Modified_Date",@mod_date) SET @sub = CreateObject("Subscriber") SetObjectProperty(@sub,"EmailAddress", @email) SetObjectProperty(@sub,"SubscriberKey", @email) SetObjectProperty(@sub,"Status","Active") Set @options = CreateObject("UpdateOptions") Set @save = CreateObject("SaveOption") SetObjectProperty(@save,"SaveAction","UpdateAdd") SetObjectProperty(@save,"PropertyName","*") AddObjectArrayItem(@options,"SaveOptions", @save) /* Here is where we actually update the Subscriber object */ Set @update_sub = InvokeUpdate(@sub, @update_sub_status, @update_sub_errorcode, @options) /* Set the confirmation message */ set @message = "Preferences have been updated<br><br>" /* Else, if the user unsubscribed, upsert their record accordingly (Clear the Preferences field and flag them as Mailable = "N") and log the unsubscribe */ elseif @unsubscribe == "Yes" then UpsertDE("Master_PreferenceCenter_CustomerPreferences",@email,"Mailable","Y","Preferences",@preference,"Preferences","","Last_Modified_Date",@mod_date) /* set the reason for the unsubscribe */ set @reason = "Custom Unsubscribe" /* initiate the LogUnsubEvent request */ set @lue = CreateObject("ExecuteRequest") SetObjectProperty(@lue, "Name", "LogUnsubEvent") /* configure the properties of the API object */ set @lue_prop = CreateObject("APIProperty") SetObjectProperty(@lue_prop, "Name", "SubscriberKey") SetObjectProperty(@lue_prop, "Value", @email) AddObjectArrayItem(@lue, "Parameters", @lue_prop) set @lue_prop = CreateObject("APIProperty") SetObjectProperty(@lue_prop, "Name", "EmailAddress") SetObjectProperty(@lue_prop, "Value", @email) AddObjectArrayItem(@lue, "Parameters", @lue_prop) set @lue_prop = CreateObject("APIProperty") SetObjectProperty(@lue_prop, "Name", "JobID") SetObjectProperty(@lue_prop, "Value", @jobid) AddObjectArrayItem(@lue, "Parameters", @lue_prop) set @lue_prop = CreateObject("APIProperty") SetObjectProperty(@lue_prop, "Name", "Reason") SetObjectProperty(@lue_prop, "Value", @reason) AddObjectArrayItem(@lue, "Parameters", @lue_prop) /* this is where the unsubscribe is performed */ set @lue_statusCode = InvokeExecute(@lue, @overallStatus, @requestId) /* check status/errors */ set @Response = Row(@lue_statusCode, 1) set @UStatus = Field(@Response, "StatusMessage") set @Error = Field(@Response, "ErrorCode") /* Set the confirmation message */ set @message = "You have been unsubscribed.<br>To resubscribe, click the submit button below.<br><br>" else endif
For the form on the page, you can style it as desired, but within the
<form> tags, the below code will need to be included. This code uses an AMPscript loop to display all of the preferences defined in the "Master_PreferenceCenter_PreferencesList" reference data extension. Within the loop, we also utilize a lookup to define whether or not the checkbox should be checked based on the user's previously defined preferences (if available).
Again, it might be helpful to lookup some of the functions being used in the below script if you aren’t familiar with them quite yet:
Process Loops - https://ampscript.guide/process-loops/
Lookup - https://ampscript.guide/lookup/
LookupRows - https://ampscript.guide/lookuprows/
LookupOrderedRows - https://ampscript.guide/lookuporderedrows/
Output - https://ampscript.guide/output/
%%[ var @preferences, @preference_name, @header, @i, @rows, @row, @checked /* The @preferences variable pulls in the value of the user's "Preferences" field in the master preference center data extension, if available. The @userlookupcount looks on the master preference center data extension to see if the record exists and sets the @existinguser variable accordingly. */ set @preferences = Lookup("Master_PreferenceCenter_CustomerPreferences","Preferences","Email_Address",@email) set @userlookupcount = LookupRows("Master_PreferenceCenter_CustomerPreferences","Email_Address",@email) if @userlookupcount > 0 then set @existinguser = "Y" else set @existinguser = "N" else endif /* This sets up the loop to bring in all preferences listed in the preferences reference data extension. The @header field defines what the text above the preferences checkbox will be. Set this to whatever you'd like. */ set @rows = LookupOrderedRows("Master_PreferenceCenter_PreferencesList",0, "Priority asc, Preference_Name","Key","X") set @header = "<b>Send me emails about:</b><br><br>" for @i = 1 TO RowCount(@rows) DO /* If this is the first row pulled, place the @header variable above the checkboxes */ if @i == 1 then output(v(@header)) else endif /* Pulls in the preference_name field and applies it to the @preference_name variable */ set @row = Row(@rows,@i) set @preference_name = Field(@row, "Preference_Name") /* Sets the checkbox as "checked" on the page if the user has previously opted-in to the preference or if the user has not yet defined their preferences */ if IndexOf(@preferences,@preference_name) > 0 OR @existinguser == "N" then set @checked = "checked" else set @checked = "" endif /* Sets the checkbox input field that will be displayed. All of the checkboxes will have the same name and will be submitted as one comma-delimited value */ output(concat("<input type='checkbox' name='preference' ",@checked," value='",@preference_name,"'>",@preference_name,"<br>")) next @i ]%%
Now your preference center data extensions are created and your page is setup and (hopefully) looking good and functioning as expected. But, how do users access this? Like we mentioned above, we’ve set this up so that users can only access via a link in one of your emails. So, for this, we’ll be using the CloudPagesURL function in a link in our email. When users click on the link, they’ll be taken to our preferences page and they’ll have all of the necessary information (email address, job id) with them. Here is an example link for your emails:
<a href="%%=CloudPagesURL(123)=%%">Update Preferences</a>
The next, and final step is optional and depends on your data setup within Salesforce Marketing Cloud. If you have a customer master data extension and would like to keep this data within your customer master data extension, then simply setup an automation to run hourly. Within the automation have a SQL query that updates your customer master with the values from the master preference center data extension.
And then that’s it, you’re done. Your customers are now able to easily update their preferences, you can easily segment them based on those preferences, and you can even somewhat easily update the preferences you offer customers.