Working with Salesforce Multi-Select Picklists in AMPscript

Custom Multi-Select Picklists in Sales/Service Cloud allow users to select one or more values from a predefined list:

When a user picks more than one value, the selected values show in a field separated by a semicolons:

The values will be in the same, semicolon-delimited format, if you synchronize the picklist field into Marketing Cloud and store the data in a Data Extension. This means that you will have to break each of those fields up into separate values to be able to use them in your script.

My example multi-select picklist in Sales Cloud is used for storing subscriber preferences and has five possible values:

  • Events
  • Newsletters
  • Promotions
  • Special Offers
  • Surveys

In this tutorial, we will first pull the multi-select picklist data onto a CloudPage to display it for the subscriber as an HTML form, where they can pick which of the above they are interested in, and once they submit the form, we will write that data back into Sales Cloud.

Splitting a string by a delimiter

First, let’s pull the data into our script. In the example below, I will use the RetrieveSalesforceObjects() function to pull the data onto a CloudPage directly from Sales/Service Cloud. Depending on your use case, you might want to use the Lookup() or LookupRows() functions instead, if you are working with Synchronized Data Extensions.

My multi-select picklist field is called Preferences__c and it’s on the Contact object:

%%[
set @subscriberKey = AttributeValue(_subscriberKey)
set @subscriberrows = retrievesalesforceobjects(
"contact",
"firstname,lastname,email,preferences__c",
"id", "=", @subscriberKey)
if rowcount(@subscriberrows) == 1 then
set @subscriberrow = row(@subscriberrows, 1)
set @firstname = field(@subscriberrow, "firstname")
set @lastname = field(@subscriberrow, "lastname")
set @email = field(@subscriberrow, "email")
set @preferences__c = field(@subscriberrow, "preferences__c")
endif
]%%
%%=v(@preferences__c)=%%

When you output the data using %%=v(@preferences__c)=%%, you will see it in exactly the same semicolon-delimited format as mentioned earlier:

In order to split multi-select picklist data into separate values and to be able to assign those values to AMPscript variables, we will use the BuildRowSetFromString() function. The BuildRowSetFromString function will create a rowset from a character string by splitting the string at the specified delimiter – in our case, a semicolon.

As a first step, we will check if our @preferences__c variable has any data inside by using the Empty() function. If the variable is not empty, we can proceed with the BuildRowSetFromString function:

%%[
set @subscriberKey = AttributeValue(_subscriberKey)
set @subscriberrows = retrievesalesforceobjects(
"contact",
"firstname,lastname,email,preferences__c",
"id", "=", @subscriberKey)
if rowcount(@subscriberrows) == 1 then
set @subscriberrow = row(@subscriberrows, 1)
set @firstname = field(@subscriberrow, "firstname")
set @lastname = field(@subscriberrow, "lastname")
set @email = field(@subscriberrow, "email")
set @preferences__c = field(@subscriberrow, "preferences__c")
endif
if not empty(@Preferences__c) then
set @rs = buildrowsetfromstring(@preferences__c,';')
Output(rowcount(@rs))
endif
]%%

Output(rowcount(@rs)) will output the number of values contained in our multi-select picklist field.

Now let’s add a loop to iterate through the rowset and match the values in our script with multi-select picklist values from Saleforce. Remember, that the more picklist values you have in Salesforce, the more complicated your loop will get.

The biggest challenge we need to tackle in our script, is the fact that we never know how many and which values each subscriber will have assigned to them. That’s why we need to iterate through all of the values and use a conditional statement to check if each of the values found matches a picklist value we know exists in Salesforce.

Here’s how the script will look like once we add our loop with the conditional statements:

%%[
set @subscriberKey = AttributeValue(_subscriberKey)
set @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"FirstName,LastName,Email,Preferences__c",
"Id", "=", @subscriberKey )
if RowCount(@subscriberRows) == 1 then /* there should only be one row */
var @subscriberRow, @firstName, @lastName, @email
set @subscriberRow = Row(@subscriberRows, 1)
set @firstName = Field(@subscriberRow, "FirstName")
set @lastName = Field(@subscriberRow, "LastName")
set @email = Field(@subscriberRow, "Email")
set @Preferences__c = Field(@subscriberRow, "Preferences__c")
IF NOT EMPTY(@Preferences__c) THEN
SET @rs = BuildRowsetFromString(@Preferences__c,';')
IF rowcount(@rs) > 0 THEN
FOR @i=1 TO rowcount(@rs) DO
SET @val = Field(Row(@rs,@i),1)
IF @val == "Surveys" THEN
SET @Surveys = true
ELSEIF @val == "Events" THEN
SET @Events = true
ELSEIF @val == "Newsletters" THEN
SET @Newsletters = true
ELSEIF @val == "Promotions" THEN
SET @Promotions = true
ELSEIF @val == "Special Offers" THEN
SET @Offers = true
ENDIF
NEXT @i
ELSE
SET @Newsletters = ""
SET @Events = ""
SET @Offers = ""
SET @Survays = ""
SET @Promotions = ""
ENDIF
ENDIF
endif
]%%
@Surveys: %%=v(@Surveys)=%%<br>
@Events: %%=v(@Events)=%%<br>
@Newsletters: %%=v(@Newsletters)=%%<br>
@Promotionss: %%=v(@Promotions)=%%<br>
@Offers: %%=v(@Offers)=%%<br>

Above script will display true for all values where it found a match.

Let’s now add an HTML form and display our picklist data as checkboxes.

HTML checkboxes and AMPscript

We will use AMPscript and the values retrieved using the BuildRowSetFromString function to conditionally mark checkboxes as checked if there is a matching value in Salesforce. In HTML forms, the checked attribute specifies that an element should be pre-selected when the page loads (for type=”checkbox” or type=”radio”):

<input type="checkbox" checked>

For each of the checkboxes representing the multi-select picklist values, we will add an AMPscript variable to conditionally add the checked attribute when the value is found in our rowset. Let’s also add the basic subscriber information like First Name, Last Name and Email to the form:

<h2>Please fill in the form:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" required="" value="%%=v(@firstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" required="" value="%%=v(@lastName)=%%"><br>
<label>Email: </label><input type="text" name="email" required="" value="%%=v(@email)=%%"><br><br>
<h2>Update your subscriptions:</h2>
<input name="newsletters" type="checkbox" %%=v(@Newsletters)=%%><label>Newsletters</label><br>
<input name="events" type="checkbox" %%=v(@Events)=%%><label>Events</label><br>
<input name="offers" type="checkbox" %%=v(@Offers)=%%><label>Special Offers</label><br>
<input name="surveys" type="checkbox" %%=v(@Surveys)=%%><label>Surveys</label><br>
<input name="promotions" type="checkbox" %%=v(@Promotions)=%%><label>Promotions</label><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>

Now we need to adjust or script to display the word checked instead of true in case there is a match:

%%[
set @subscriberKey = AttributeValue(_subscriberKey)
set @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"FirstName,LastName,Email,Preferences__c",
"Id", "=", @subscriberKey )
if RowCount(@subscriberRows) == 1 then /* there should only be one row */
var @subscriberRow, @firstName, @lastName, @email
set @subscriberRow = Row(@subscriberRows, 1)
set @firstName = Field(@subscriberRow, "FirstName")
set @lastName = Field(@subscriberRow, "LastName")
set @email = Field(@subscriberRow, "Email")
set @Preferences__c = Field(@subscriberRow, "Preferences__c")
IF NOT EMPTY(@Preferences__c) THEN
SET @rs = BuildRowsetFromString(@Preferences__c,';')
IF rowcount(@rs) > 0 THEN
FOR @i=1 TO rowcount(@rs) DO
SET @val = Field(Row(@rs,@i),1)
IF @val == "Surveys" THEN
SET @Surveys = "checked"
ELSEIF @val == "Events" THEN
SET @Events = "checked"
ELSEIF @val == "Newsletters" THEN
SET @Newsletters = "checked"
ELSEIF @val == "Promotions" THEN
SET @Promotions = "checked"
ELSEIF @val == "Special Offers" THEN
SET @Offers = "checked"
ENDIF
NEXT @i
ELSE
SET @Newsletters = ""
SET @Events = ""
SET @Offers = ""
SET @Survays = ""
SET @Promotions = ""
ENDIF
ENDIF
endif
]%%
<h2>Please fill in the form:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" required="" value="%%=v(@firstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" required="" value="%%=v(@lastName)=%%"><br>
<label>Email: </label><input type="text" name="email" required="" value="%%=v(@email)=%%"><br><br>
<h2>Update your subscriptions:</h2>
<input name="newsletters" type="checkbox" %%=v(@Newsletters)=%%><label>Newsletters</label><br>
<input name="events" type="checkbox" %%=v(@Events)=%%><label>Events</label><br>
<input name="offers" type="checkbox" %%=v(@Offers)=%%><label>Special Offers</label><br>
<input name="surveys" type="checkbox" %%=v(@Surveys)=%%><label>Surveys</label><br>
<input name="promotions" type="checkbox" %%=v(@Promotions)=%%><label>Promotions</label><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>

Now you should be able to correctly display the multi-select picklist data for each subscriber visiting your CloudPage.

Update Multi-Select Picklist values in Salesforce using AMPscript

Once the subscriber makes changes to their preferences and clicks on the Submit button, the page will reload and the form parameters will be posted back to the same page. If you would like to learn more about this approach of working with forms on CloudPages, take a look at one of my earlier articles: Create a Sales Cloud-integrated lead capture form using AMPscript.

Now we can add the script to process the posted form data and update it in Salesforce:

SET @preferences = CONCAT(
Iif(RequestParameter("newsletters") == "on", "Newsletters;", ""),
Iif(RequestParameter("events") == "on", "Events;", ""),
Iif(RequestParameter("offers") == "on", "Special Offers;", ""),
Iif(RequestParameter("surveys") == "on", "Surveys;", ""),
Iif(RequestParameter("promotions") == "on", "Promotions", ""),
)
if not Empty(@preferences) then
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", RequestParameter("subkey"),
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Preferences__c", @preferences)
else
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", RequestParameter("subkey"),
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"fieldsToNull", "Preferences__c")
endif

To pass the data back to Salesforce, we need to transform the posted form data back into the initial form: a string with semicolon-delimited values. In order to achieve this, I have used the Iif() function to conditionally check which values have been selected in the form (a selected checkbox will return the value of on). If a parameter returns the on value, we substitute it with the corresponding picklist value and concatenate all the returned values together into one string to pass them to Salesforce.

The full script

Here is the full script, including a try/catch statement for debugging:

<script runat="server">
Platform.Load("Core","1.1.1");
try{
</script>
%%[
IF RequestParameter("submitted") == true THEN
SET @preferences = CONCAT(
Iif(RequestParameter("newsletters") == "on", "Newsletters;", ""),
Iif(RequestParameter("events") == "on", "Events;", ""),
Iif(RequestParameter("offers") == "on", "Special Offers;", ""),
Iif(RequestParameter("surveys") == "on", "Surveys;", ""),
Iif(RequestParameter("promotions") == "on", "Promotions", ""),
)
if not Empty(@preferences) then
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", RequestParameter("subkey"),
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Preferences__c", @preferences)
else
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", RequestParameter("subkey"),
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"fieldsToNull", "Preferences__c")
endif
]%%
Your preferences have been updated.
%%[ELSE
set @subscriberKey = AttributeValue(_subscriberKey)
set @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"FirstName,LastName,Email,Preferences__c",
"Id", "=", @subscriberKey)
if RowCount(@subscriberRows) == 1 then
var @subscriberRow, @firstName, @lastName, @email
set @subscriberRow = Row(@subscriberRows, 1)
set @firstName = Field(@subscriberRow, "FirstName")
set @lastName = Field(@subscriberRow, "LastName")
set @email = Field(@subscriberRow, "Email")
set @Preferences__c = Field(@subscriberRow, "Preferences__c")
IF NOT EMPTY(@Preferences__c) THEN
SET @rs = BuildRowsetFromString(@Preferences__c,';')
IF rowcount(@rs) > 0 THEN
FOR @i=1 TO rowcount(@rs) DO
SET @val = Field(Row(@rs,@i),1)
IF @val == "Surveys" THEN
SET @Surveys = "checked"
ELSEIF @val == "Events" THEN
SET @Events = "checked"
ELSEIF @val == "Newsletters" THEN
SET @Newsletters = "checked"
ELSEIF @val == "Promotions" THEN
SET @Promotions = "checked"
ELSEIF @val == "Special Offers" THEN
SET @Offers = "checked"
ENDIF
NEXT @i
ELSE
SET @Newsletters = ""
SET @Events = ""
SET @Offers = ""
SET @Survays = ""
SET @Promotions = ""
ENDIF
ENDIF
endif
]%%
<h2>Please fill in the form:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" required="" value="%%=v(@firstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" required="" value="%%=v(@lastName)=%%"><br>
<label>Email: </label><input type="text" name="email" required="" value="%%=v(@email)=%%"><br><br>
Pref: %%=v(@Preferences__c)=%%<br><br>
<h2>Update your subscriptions:</h2>
<input name="newsletters" type="checkbox" %%=v(@Newsletters)=%%><label>Newsletters</label><br>
<input name="events" type="checkbox" %%=v(@Events)=%%><label>Events</label><br>
<input name="offers" type="checkbox" %%=v(@Offers)=%%><label>Special Offers</label><br>
<input name="surveys" type="checkbox" %%=v(@Surveys)=%%><label>Surveys</label><br>
<input name="promotions" type="checkbox" %%=v(@Promotions)=%%><label>Promotions</label><br>
<input name="subkey" type="hidden" value="%%=v(@subscriberKey)=%%"><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>
%%[ ENDIF ]%%
<script runat="server">
}catch(e){
Write(Stringify(e));
}
</script>

To see this script in action, visit my CloudPage here.


Questions? Comments?

Leave a comment below or email me at zuzanna@sfmarketing.cloud.

Troubleshooting Marketing Cloud Connect Business Unit mapping

This quick tutorial will come in hand for anyone who is making changes to their Marketing Cloud Connect and Business Unit setup. Whether you’re trying to remove Business Unit access for an existing Salesforce Marketing Cloud API user or you’re disconnecting your current connection and replacing it with a brand new one, it’s possible that you will come across the following error:

Sorry, something went wrong.
We weren’t able to retrieve your business units. Check to make sure you have permission to access these business units and try again.
Need more help? Turn on logging and open a support case in the Help and Training portal.

Above message indicates that the previous connection in Salesforce has not been fully removed and the connector still tries to retrieve the business units that were set up earlier on top of the new business units.

Removing the Configuration will not always help with removing Business Unit mapping, so in case you see the error message, try the following:

  1. In Salesforce Marketing Cloud, make sure that the SFMC API user has access to the correct Business Units.
  2. In Sales/Service Cloud, navigate to Setup > Tabs
  3. Under Custom Object Tabs, click New
  4. From the Object drop-down menu, choose Business Unit and pick any Tab Style that you like:

5. Click Next & Next & Save. You should now be able to see the new tab in both Classic and Lightening interfaces:

6. Click on the Business Units tab and choose to view All:

  1. Delete unwanted Business Units from the list using the Del Action.
  2. Go to the Marketing Cloud tab, click on Connect to Marketing Cloud and log in with the SFMC API user credentials.

When you click on Manage Business Units, you should now be able to choose Business Units from the correct list of Business Units that your SFMC API user has access to.


Questions? Comments?

Leave a comment below or email me at zuzanna@sfmarketing.cloud.

Manage Contacts and Leads in Marketing Cloud Journeys directly from your Salesforce org

This week Salesforce Labs has released a Lightning Component which, no doubt, will make the life easier for anyone working with Journey Builder and Marketing Cloud Connect.

If you have multiple Journeys set up in your account with different Salesforce Entry Events, you probably know the pain of finding out which Sales/Service Cloud Contacts and Leads are currently in the Journeys. And if you need to eject a Contact/Lead from a Journey immediately? You either end up doing it in POSTMAN or developing your own app on CloudPages just for that purpose.

Marketing Cloud Journeys for Salesforce is a Lightning Component which can be added to the Contact, Lead, Case or Account page and it will display the list of Salesforce Marketing Cloud Journeys that the subscriber is currently in, along with a stop button to allow instant removal of said subscriber from the Journey.

It’s available for free from AppExchange: Marketing Cloud Journeys for Salesforce, and you need to have Admin permissions in both Sales/Service Cloud and in Marketing Cloud to be able to install it.

Here’s the high level overview of the installation process (detailed guide can be found here):

  1. Install the managed package from AppExchange: Marketing Cloud Journeys for Salesforce
  2. Create a Package in Marketing Cloud with the component: API integration > Web App
  3. In your Salesforce org, configure Auth. Provider with type Open ID Connect and paste all the details of the Marketing Cloud API package created in the previous step
  4. In your Salesforce org, configure the Named Credential and test the Authentication Flow on save
  5. Add the Lightning Component to Record Pages by opening any Contact, Lead, Case or Account and clicking on the Setup menu at the top right and choosing Edit Page

One important prerequisite, which is not clearly described in the installation guide, is the requirement to have a “Contact Key” field on the object for which you want to display the component. You do not need to create that field separately – it’s enough that you just point to the field containing the ID used as Subscriber Key in Marketing Cloud, for example Lead Id or Contact Id:

Now the updated page will display the component and list all the Journey names and their current active version, in which the subscriber is present:

It will also give you the option to eject the subscriber from a selected Journey, or from all active Journeys:

Upon clicking on the Stop/Stop All Journeys option, you will be prompted to confirm:

Conslusion

Salesforce Labs is closing a major gap in the cross-cloud functionality with this small, yet powerful and much needed addition. The component has proven to work correctly in the few simple tests I gave it and I would recommend anyone using Salesforce Marketing Cloud’s Journey Builder with Sales/Service data to also give it a go.


Questions? Comments?

Leave a comment below or email me at zuzanna@sfmarketing.cloud.

Custom profile and subscription center integrated with Sales/Service Cloud

In this tutorial, you will learn how to create a simple, yet fully customizable profile and subscription centre integrated with Sales and Service Cloud. Bear in mind, that the script utilizes a couple of standard and custom Sales/Service Cloud fields, which you will need to adjust based on your Salesforce CRM setup – the full list can be found here.

What are Profile and Subscription Centers?

Each instance of Salesforce Marketing Cloud comes with a standard, predefined Profile Center and Subscription Center.

The profile center is a webpage where subscribers can enter and maintain the personal information that you keep about them. When you import a list, you can import attribute values for your subscribers that appear when a subscriber visits the profile center. The subscriber can update their information on this page and provide additional information.

A subscription center is a web page where a subscriber can control the messages they receive from your organization. The lists (including publication lists) you identify as public in the application are available for a subscriber to opt in to on the subscription center.

If you’re using Marketing Cloud Connect to integrate with Sales/Service Cloud, you can use the standard Profile Center based on the Marketing Cloud attributes that are mapped to fields in Sales or Service Cloud. Marketing Cloud Connect documentation states, that changes made in the Profile Center update Salesforce contact and lead data – unfortunately I haven’t been able to confirm this with my tests.

Another limitation which is very hard to overcome is the look and feel of the standard Profile and Subscription Center. If you enable Marketing Cloud BrandBuilder, you will be able to customize the color scheme of your Profile and Subscription Center based on the colors of the logo you upload – but you won’t be able to change the layout or the fonts used.

Sooner or later, most Marketing Cloud users switch to custom Profile and Subscription Centers, as the standard one is not enough when it comes to more complex use cases.

Anatomy of a Subscription Center

A Subscription Center is nothing else but an HTML form. The data passed in the form is then processed by a script – in this tutorial, we will use AMPscript. If you’ve never created a form before, you might want to check out this article first: Create a Sales Cloud-integrated lead capture form using AMPscript.

In the form, you can include any fields from any of the relevant Sales/Service Cloud objects, but to make it more simple as we go, for now, we will use three standard fields from the Contact object: FirstName, LastName and Email. Additionally, I have added three read-only checkboxes to the Contact object for subscription management: Newsletter, Offers and Events. [Why are they read-only? In this tutorial, you will learn how to allow subscribers to make changes to their preferences and sync those changes back to Salesforce. The process of managing the subscriptions by a Salesforce user and syncing the changes back to Marketing Cloud is a separate procedure and won’t be addressed in this article.] I also made sure that the Email Opt Out flag is visible on a Contact page layout in Sales Cloud:

Here is a reference of all the fields from the Contact object, which we will use in our script:

Field LabelAPI Field NameTypeLength
Contact IDIdid18
EmailEmailemail80
Email Opt OutHasOptedOutOfEmailboolean
Name FirstNamestring80
Name LastNamestring80
NewsletterNewsletter__cboolean
EventsEvents__cboolean
OffersOffers__cboolean

Let’s now create a simple HTML form which contains all of the above fields:

<html>
<body>
<h2>Update your profile:</h2>
<form>
<label>First name: </label><input type="text" name="firstname"><br>
<label>Last name: </label><input type="text" name="lastname"><br>
<label>Email: </label><input type="text" name="email"><br>
<h2>Update your subscriptions:</h2>
<label>Newsletter: </label><input name="newsletter" type="checkbox"><br>
<label>Events: </label><input name="events" type="checkbox"><br>
<label>Offers: </label><input name="offers" type="checkbox"><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>

Since we want the form to be pre-populated with subscriber data from Sales/Service Cloud, we need to add the RetrieveSalesforceObjects function to pull the data, and display the retrieved data as field values:

%%[
SET @contactId = _subscriberkey
IF NOT EMPTY(@contactId) THEN
/* fetch data from Sales Cloud to show in the form */
SET @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"Id,FirstName,LastName,Email,Newsletter__c,Events__c,Offers__c,HasOptedOutOfEmail",
"Id", "=", @contactId
)
IF RowCount(@subscriberRows) > 0 THEN
SET @row = row(@subscriberRows,1)
SET @FirstName = field(@row,"FirstName")
SET @LastName = field(@row,"LastName")
SET @Email = field(@row,"Email")
SET @Newsletter = field(@row,"Newsletter__c")
SET @Events = field(@row,"Events__c")
SET @Offers = field(@row,"Offers__c")
SET @HasOptedOutOfEmail = field(@row,"HasOptedOutOfEmail")
IF @Newsletter == true THEN SET @Newsletterchk = "checked" ENDIF
IF @Events == true THEN SET @Eventschk = "checked" ENDIF
IF @Offers == true THEN SET @Offerschk = "checked" ENDIF
]%%
<h2>Update your profile:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" value="%%=v(@FirstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" value="%%=v(@LastName)=%%"><br>
<label>Email: </label><input type="text" name="email" value="%%=v(@Email)=%%"><br>
<h2>Update your subscriptions:</h2>
<label>Newsletter: </label><input name="newsletter" type="checkbox" %%=v(@Newsletterchk)=%%><br>
<label>Events: </label><input name="events" type="checkbox" %%=v(@Eventschk)=%%><br>
<label>Offers: </label><input name="offers" type="checkbox" %%=v(@Offerschk)=%%><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>
%%[ELSE]%%
Sorry, something went wrong (no records found).
%%[ENDIF ELSE]%%
Sorry, something went wrong (missing subscriber key).
%%[ENDIF]%%

You will also notice in the above script, that we have added a form action. The RequestParameter('PAGEURL') function reloads the page when the form is submitted, posting the form parameters back to the same page. You can, of course, post the form data to another page and process it there, but for the purpose of this tutorial, let’s keep everything on one page so that it’s easier to copy and paste.

After the form is submitted and posted, the data can be retrieved by using the RequestParameter AMPscript function. We will then update the data in Sales/Service Cloud using the UpdateSingleSalesforceObject function. Let’s add them now:

%%[
SET @contactId = _subscriberkey
IF RequestParameter("submitted") == true THEN
/* update contact */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Newsletter__c", Iif(RequestParameter("newsletter") == "on", "true", "false"),
"Events__c", Iif(RequestParameter("events") == "on", "true", "false"),
"Offers__c", Iif(RequestParameter("offers") == "on", "true", "false")
)
Output(v('<font color="green">Contact has been updated succesfully.</font><br>'))
ENDIF
IF NOT EMPTY(@contactId) THEN
/* fetch data from Sales Cloud to show in the form */
SET @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"Id,FirstName,LastName,Email,Newsletter__c,Events__c,Offers__c,HasOptedOutOfEmail",
"Id", "=", @contactId
)
IF RowCount(@subscriberRows) > 0 THEN
SET @row = row(@subscriberRows,1)
SET @FirstName = field(@row,"FirstName")
SET @LastName = field(@row,"LastName")
SET @Email = field(@row,"Email")
SET @Newsletter = field(@row,"Newsletter__c")
SET @Events = field(@row,"Events__c")
SET @Offers = field(@row,"Offers__c")
SET @HasOptedOutOfEmail = field(@row,"HasOptedOutOfEmail")
IF @Newsletter == true THEN SET @Newsletterchk = "checked" ENDIF
IF @Events == true THEN SET @Eventschk = "checked" ENDIF
IF @Offers == true THEN SET @Offerschk = "checked" ENDIF
]%%
<h2>Update your profile:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" value="%%=v(@FirstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" value="%%=v(@LastName)=%%"><br>
<label>Email: </label><input type="text" name="email" value="%%=v(@Email)=%%"><br>
<h2>Update your subscriptions:</h2>
<label>Newsletter: </label><input name="newsletter" type="checkbox" %%=v(@Newsletterchk)=%%><br>
<label>Events: </label><input name="events" type="checkbox" %%=v(@Eventschk)=%%><br>
<label>Offers: </label><input name="offers" type="checkbox" %%=v(@Offerschk)=%%><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit">
</form>
%%[ELSE]%%
Sorry, something went wrong (no records found).
%%[ENDIF ELSE]%%
Sorry, something went wrong (missing subscriber key).
%%[ENDIF]%%

Congratulations – you just built the first, simple version of your profile and subscription center!

Now let’s analyze the above script. The first thing that you will notice is that all the AMPscript is placed at the beginning of the document, above the form – that’s because the form data first has to be posted before it can be processed, and upon posting the data, the page reloads and starts resolving the script from top-down. If you need more clarification on this concept, click here.

If the form has been submitted, we will update the Sales/Service Cloud contact with the new data that was posted upon form submission. The form data is retrieved using the RequestParameter function, which is used in the @updateRecord variable inside the UpdateSingleSalesforceObject function.

You will also notice, that for each of the checkboxes, I have added the following in-line IF function: Iif(RequestParameter("newsletter") == "on", "true", "false"). That is because the checkbox passed from the HTML form will have a value of either on or off, while to pass it to Sales/Service Cloud, we need to convert it to a boolean value of true or false.

Unsubscribe From All option

To make the functionality of our custom subscription and profile center similar to the standard one, we can add the Unsubscribe From All and Resubscribe options. The Unsubscribe From All option should not only set all the subscription-related flags in Sales Cloud to false, but also set the subscriber’s status to Unsubscribed in Salesforce Marketing Cloud’s All Subscribers list. To achieve this, we need to log an UnsubEvent. In order to do that, we will add an additional button to the form (Unsubscribe From All) and a hidden parameter which will be passed if someone clicks this button (name="unsub" type="hidden" value="true"). After submitting the form, if unsub value equals true, we will log the UnsubEvent in addition to updating the flags in Sales/Service Cloud. The below is a simplified version of logging an UnsubEvent and it will unsubscribe the contact from all BUs – if you would like to modify it to your use case, refer to this article: Unsubscribe and Log an UnsubEvent with a LogUnsubEvent Execute Call. Here’s what we need to add to our script to make this part work:

IF RequestParameter("submitted") == true AND RequestParameter("unsub") == true THEN
/* update contact in Sales Cloud */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"Newsletter__c", "false",
"Events__c", "false",
"Offers__c", "false",
"HasOptedOutOfEmail", "true"
)
/* log unsubscribe event to mark as unsubscribed in All Subscribers */
SET @reason = "Unsubscribed through custom subscription center"
SET @lue = CreateObject("ExecuteRequest")
SETObjectProperty(@lue,"Name","LogUnsubEvent")
SET @lue_prop = CreateObject("APIProperty")
SETObjectProperty(@lue_prop, "Name", "SubscriberKey")
SETObjectProperty(@lue_prop, "Value", @contactId)
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)
SET @lue_statusCode = InvokeExecute(@lue, @overallStatus, @requestId)

Since we have the option to Unsubscribe From All, we now also have to add an option to Resubscribe. The Resubscribe option will set all the subscription-related flags in Sales Cloud to true and set the subscriber’s status to Active in Salesforce Marketing Cloud’s All Subscribers list. The Resubscribe button will appear for any contact that has the HasOptedOutOfEmail flag set to true in Sales Cloud. The button also has a hidden parameter that will be passed with the form (name="sub" type="hidden" value="true"). Upon clicking the button, we will invoke the update method on a subscriber object to set their status to Active in Marketing Cloud, as well as update their subscription-related flags in Sales Cloud:

ELSEIF RequestParameter("submitted") == true AND RequestParameter("sub") == true THEN
/* update contact in Sales Cloud */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Newsletter__c", "true",
"Events__c", "true",
"Offers__c", "true",
"HasOptedOutOfEmail", "false"
)
/* set subscriber status to active in All Subscribers */
SET @email = RequestParameter("email")
SET @Subscriber = CreateObject("Subscriber")
SetObjectProperty(@Subscriber, "SubscriberKey", @contactId)
SetObjectProperty(@Subscriber, "EmailAddress", @email)
SetObjectProperty(@Subscriber, "Status", "Active" )
SET @Status = InvokeUpdate(@Subscriber, @createErrDesc, @createErrNo, @createOpts)

Update subscriber’s data in Marketing Cloud

If you’re using Marketing Cloud Connect, a very important thing to remember when updating subscriber’s email address is to do it in both, Sales/Service Cloud and Marketing Cloud. Here is why:

If an email address for a Lead or Contact Object is updated in Sales Cloud or Service Cloud, the corresponding email address is not updated in the All Subscribers list.

Eliot Harper, THE DATA HANDBOOK – Data Architecture for Salesforce Marketing Cloud

In order to achieve this, we will add one more piece of a script to update the subscriber object. First we will check, if the subscriber’s current email address in Marketing Cloud is different from the one provided in the form, and if it is, we will update it accordingly:

/* update email in All Subscribers list */
IF RequestParameter("email") != AttributeValue("emailaddr") THEN
SET @email = RequestParameter("email")
SET @Subscriber = CreateObject("Subscriber")
SetObjectProperty(@Subscriber, "SubscriberKey", @contactId)
SetObjectProperty(@Subscriber, "EmailAddress", @email)
SET @Status = InvokeUpdate(@Subscriber, @createErrDesc, @createErrNo, @createOpts)
ENDIF

You do not need to include the above if you already have a process in place that synchronizes email address changes from Sales/Service Cloud to All Subscribers list in Salesforce Marketing Cloud. If you’d like to learn more about synching updates between the clouds, read Markus Slabina’s blog post here.

Custom subscription and profile center full script

As a final touch, to improve the experience, let’s add a confirmation message and a button that will reload the page and display the updated data in the form. Here is the final version of the script, with all of the above changes included:

<!––– Copyright © 2020 Zuzanna Jarczynska http://sfmarketing.cloud –––>
%%[
SET @contactId = _subscriberkey
IF NOT EMPTY(@contactId) THEN
/* fetch data from Sales Cloud to show in the form */
SET @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"Id,FirstName,LastName,Email,Newsletter__c,Events__c,Offers__c,HasOptedOutOfEmail",
"Id", "=", @contactId
)
IF RowCount(@subscriberRows) > 0 THEN
SET @row = row(@subscriberRows,1)
SET @FirstName = field(@row,"FirstName")
SET @LastName = field(@row,"LastName")
SET @Email = field(@row,"Email")
SET @Newsletter = field(@row,"Newsletter__c")
SET @Events = field(@row,"Events__c")
SET @Offers = field(@row,"Offers__c")
SET @HasOptedOutOfEmail = field(@row,"HasOptedOutOfEmail")
IF @Newsletter == true THEN SET @Newsletterchk = "checked" ENDIF
IF @Events == true THEN SET @Eventschk = "checked" ENDIF
IF @Offers == true THEN SET @Offerschk = "checked" ENDIF
ELSE
]%%
Sorry, something went wrong (no records found).
%%[
ENDIF
ELSE
]%%
Sorry, something went wrong (missing subscriber key).
%%[
ENDIF
IF RequestParameter("submitted") == true AND RequestParameter("unsub") == true THEN
/* update contact in Sales Cloud */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"Newsletter__c", "false",
"Events__c", "false",
"Offers__c", "false",
"HasOptedOutOfEmail", "true"
)
/* log unsubscribe event to mark as unsubscribed in All Subscribers */
SET @reason = "Unsubscribed through custom subscription center"
SET @lue = CreateObject("ExecuteRequest")
SETObjectProperty(@lue,"Name","LogUnsubEvent")
SET @lue_prop = CreateObject("APIProperty")
SETObjectProperty(@lue_prop, "Name", "SubscriberKey")
SETObjectProperty(@lue_prop, "Value", @contactId)
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)
SET @lue_statusCode = InvokeExecute(@lue, @overallStatus, @requestId)
ELSEIF RequestParameter("submitted") == true AND RequestParameter("sub") == true THEN
/* update contact in Sales Cloud */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Newsletter__c", "true",
"Events__c", "true",
"Offers__c", "true",
"HasOptedOutOfEmail", "false"
)
/* set subscriber status to active in All Subscribers */
SET @email = RequestParameter("email")
SET @Subscriber = CreateObject("Subscriber")
SetObjectProperty(@Subscriber, "SubscriberKey", @contactId)
SetObjectProperty(@Subscriber, "EmailAddress", @email)
SetObjectProperty(@Subscriber, "Status", "Active" )
SET @Status = InvokeUpdate(@Subscriber, @createErrDesc, @createErrNo, @createOpts)
/* fetch updated data to show in the form */
SET @subscriberRows = RetrieveSalesforceObjects(
"Contact",
"Id,FirstName,LastName,Email,Newsletter__c,Events__c,Offers__c,HasOptedOutOfEmail",
"Id", "=", @contactId
)
SET @row = row(@subscriberRows,1)
SET @FirstName = field(@row,"FirstName")
SET @LastName = field(@row,"LastName")
SET @Email = field(@row,"Email")
SET @Newsletter = field(@row,"Newsletter__c")
SET @Events = field(@row,"Events__c")
SET @Offers = field(@row,"Offers__c")
SET @HasOptedOutOfEmail = field(@row,"HasOptedOutOfEmail")
IF @Newsletter == true THEN SET @Newsletterchk = "checked" ENDIF
IF @Events == true THEN SET @Eventschk = "checked" ENDIF
IF @Offers == true THEN SET @Offerschk = "checked" ENDIF
ELSEIF RequestParameter("submitted") == true THEN
/* update contact */
SET @updateRecord = UpdateSingleSalesforceObject(
"Contact", @contactId,
"FirstName", RequestParameter("firstname"),
"LastName", RequestParameter("lastname"),
"Email", RequestParameter("email"),
"Newsletter__c", Iif(RequestParameter("newsletter") == "on", "true", "false"),
"Events__c", Iif(RequestParameter("events") == "on", "true", "false"),
"Offers__c", Iif(RequestParameter("offers") == "on", "true", "false")
)
/* update email in All Subscribers list */
IF RequestParameter("email") != AttributeValue("emailaddr") THEN
SET @email = RequestParameter("email")
SET @Subscriber = CreateObject("Subscriber")
SetObjectProperty(@Subscriber, "SubscriberKey", @contactId)
SetObjectProperty(@Subscriber, "EmailAddress", @email)
SET @Status = InvokeUpdate(@Subscriber, @createErrDesc, @createErrNo, @createOpts)
ENDIF
ENDIF
IF @HasOptedOutOfEmail == true THEN
]%%
<h2>Update your profile:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" value="%%=v(@FirstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" value="%%=v(@LastName)=%%"><br>
<label>Email: </label><input type="text" name="email" value="%%=v(@Email)=%%"><br>
<h2>Resubscribe</h2>
You have unsubscribed from ALL publications from <i>%%member_busname%%</i>. If you wish to resubscribe, click the Resubscribe button below.
<input name="submitted" type="hidden" value="true"><br>
<input name="sub" type="hidden" value="true"><br><br>
<input type="submit" value="Resubscribe">
</form>
%%[ELSEIF RequestParameter("submitted") == false AND NOT EMPTY(@contactId) AND RowCount(@subscriberRows) > 0 THEN]%%
<h2>Update your profile:</h2>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<label>First name: </label><input type="text" name="firstname" value="%%=v(@FirstName)=%%"><br>
<label>Last name: </label><input type="text" name="lastname" value="%%=v(@LastName)=%%"><br>
<label>Email: </label><input type="text" name="email" value="%%=v(@Email)=%%"><br>
<h2>Update your subscriptions:</h2>
<label>Newsletter: </label><input name="newsletter" type="checkbox" %%=v(@Newsletterchk)=%%><br>
<label>Events: </label><input name="events" type="checkbox" %%=v(@Eventschk)=%%><br>
<label>Offers: </label><input name="offers" type="checkbox" %%=v(@Offerschk)=%%><br>
<input name="submitted" type="hidden" value="true"><br>
<input type="submit" value="Submit"></form>
<br><br>
<h2>Unsubscribe From All</h2>
If you wish to unsubscribe from ALL publications from <i>%%member_busname%%</i>, click the Unsubscribe From All button below.
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<input name="submitted" type="hidden" value="true">
<input name="unsub" type="hidden" value="true"><br>
<input type="submit" value="Unsubscribe From All"></form>
%%[ELSEIF RequestParameter("submitted") == true AND NOT EMPTY(@contactId) THEN]%%
<h2>Your profile has been updated.</h2>
Go back to your profile.<br>
<form action="%%=RequestParameter('PAGEURL')=%%" method="post">
<br>
<input type="submit" value="Go Back"></form>
%%[ENDIF]%%

Remember, that you will need to use the CloudPagesURL function to link to the subscription center CloudPage from an email.

In order to see how the above script works, feel free to play around with one of my test contacts in the test version of the subscription and profile center that I set up on a CloudPage: https://pub.s10.exacttarget.com/

Here are some resources if you would like to read further about subscription management best practices:

  • Here you will find an interesting concept of creating Campaigns for each subscription and adding the Contact as a Campaign Member
  • Here you can read how Effective Email Preference Centers Help Keep Subscribers Active and Engaged
  • Solve problems using the subscription-center and preference-center tags on Salesforce Stack Exchange

Questions? Comments?

Leave a comment below or email me at zuzanna@sfmarketing.cloud.

Troubleshooting Journey Builder Integration with Debug Logs

If you have a Salesforce Data Entry Event set up in Journey Builder that is not injecting records as intended, it is necessary to look into Debug Logs to determine what the cause may be. Here is a step-by-step guide to setting up and initiating Debug Logs for Journey Builder. The whole process takes part in Sales/Service Cloud, so ensure you have Admin permissions to be able to access Setup.

Turn on Marketing Cloud Connect request logs

  1. In Sales/Service Cloud, click on the Marketing Cloud tab in the Salesforce Classic interface
  2. Click Configure Marketing Cloud Connector on the right
  3. In the Notifications section, click Initiate Log

Turn on Debug Logs

  1. Go to Setup and in the Quick Find, search for Debug Logs
  2. In the User Trace Flag section, click New
  3. Set Traced Entity Type = User
  1. Set Traced Entity Name to the user that will be creating/updating the record which fails being injected into the journey
  2. Start and Expiration Dates should be set to start immediately and expire in the next hour, as the Marketing Cloud Connect activities can be logged only for 60 minutes at a time
  3. Click New Debug Level
  4. Give it a name and make sure there are no spaces
  1. In the level settings, set Workflow = Finer and Callouts = Finest, leave the rest of the settings as they are and Save
  1. Click Save and make sure your new Debug Level shows up in the Debug Level field, then click Save.

Create/Update your record

Now make sure that you are logged in as the User who was earlier selected in the Traced Entity Name and create or update your record so that it meets the entry criteria of the Journey. Document the date and time when the record was created or updated, and the ID of the record.

Parse the Debug Logs

Go to Setup and in the Quick Find, search for Debug Logs. The Debug Logs will be below the User Trace Flags section. There might be several logs, depending on the actions of the user, so having the timestamp that the record was created or updated will help narrow it down.

Use the View link to open the log(s) in a new tab. Search for JBSystemFlow_(object name) (i.e. JBSystemFlow_Contact). If you get a result, check the subsequent line with new_object in it to see if the Id lines up with the record created earlier, that should have been injected into the Journey. If so, you have found the correct log.

Next, search for a line that starts with FLOW_START_INTERVIEW_BEGIN and has JBSystemFlow_(Object). The lines between this and FLOW_START_INTERVIEW_END…JBSystemFlow_(Object) will give you all of the data in the fields for the new and old objects (old objects will only have data if a record is updated), plus all of the Decisions that the record went through in the associated Flow, plus the results for each outcome.

Here’s an example:

16:23:37.142 (150821372)|FLOW_START_INTERVIEW_BEGIN|8557980ff96eae468bac8498796162d5d1cd65-6ef6|JBSystemFlow_Lead
16:23:37.142  16:23:37.142
(151572909)|FLOW_ELEMENT_BEGIN|8557980ff96eae468bac8498796162d5d1cd65-6ef6|FlowDecision|JB_SalesforceObj2d95094362a61233d7e7d05c70c5bd3a_Decision 16:23:37.142
(151749528)|FLOW_RULE_DETAIL|8557980ff96eae468bac8498796162d5d1cd65-6ef6|SalesforceObj2d95094362a61233 (151839236)|FLOW_START_INTERVIEW_END|8557980ff96eae468bac8498796162d5d1cd65-6ef6|JBSystemFlow_Lead

Each FLOW_ELEMENT_BEGIN/FLOW_ELEMENT_END and rows in between will show information about specific Decisions in the Flow, their Outcomes, and the results of those Outcomes.

Under the FLOW_START_INTERVIEW_END…JBSystemFlow(Object) line, if everything went well with the Flow, you will see a line for FLOW_BULK_ELEMENT_BEGIN…FireJBTrigger. If you see this line, unless there are several errors directly below, Salesforce ran the APEX job to fire the Entry Event. At this point, you would check the Marketing Cloud Connect logs to see if there was an issue with completing the call (click here to skip to this section of the article). 

If you don’t see the FireJBTrigger line, copy the lines with decisions to a notepad.

Find the Flow associated with your Journey

Go to Setup, search for Flows in the Quick Find box and click Flows. Click Open next to the name of the Flow from the Debug Log (i.e. JBSystemFlow_Lead).

Once the page loads, you can drag the different nodes out to see the flow of the Decisions and Jobs. Note, that there is one Flow per Object that has at least one active Journey, which means there can be multiple Journeys referenced in one flow if more than one Journey is active on that Object. If you have a few different Journeys using, for example, Lead as the Object, you will have Decisions for the separate criteria. Sometimes a record may not pass the criteria for the Journey you are looking at but will continue through the Flow to see if it meets the criteria of other Journeys.

You will now have to find a Decision, where Name in the Decision lines up with the name at the end of the FLOW_ELEMENT_BEGIN. The second line after FLOW_ELEMENT_BEGIN, FLOW_RULE_DETAIL, will give you the name of the Outcome, and the results. The details of the Outcomes show up in the lower half of the Decision properties in the Flow. Focus only on the lines from the Debug Log that end with false, as those are the ones that did not pass the criteria. Here’s an example:

16:23:37.142 (151572909)|FLOW_ELEMENT_BEGIN|8557980ff96eae468bac8498796162d5d1cd65-6ef6|FlowDecision|JB_SalesforceObj2d95094362a61233d7e7d05c70c5bd3a_Decision 16:23:37.142 (151749528)|FLOW_RULE_DETAIL|8557980ff96eae468bac8498796162d5d1cd65-6ef6|SalesforceObj2d95094362a61233d7e7d05c70c5bd3a|false 

Once you are able to match the Decision Outcome(s) from the Flow canvas with the Decision(s) from Debug Log, you will be able to find out which criteria were not met and why the Flow stopped without injecting the record to the Journey.

Check the Marketing Cloud Connect logs

  1. Navigate to the Documents object in the Salesforce Classic interface
  2. Select the Marketing Cloud Documents folder
  3. Click Go!
  1. To view the log, select the log with the corresponding date

In the log, look out for any exceptions, here’s an example:

*****2019-10-08 09:51:24.680|LOG STARTED 
*****2019-10-08 09:51:24.680|PROCESS|SecureSettingsManager 
*****2019-10-08 09:51:24.868|EXCEPTION|Expired access/refresh token.

If you see an exception related to the access token, resolve it by clearing out User Mappings and OAuth Tokens in Marketing Cloud Connect.

If there are other exceptions or errors visible in the log which you are unable to interpret yourself, it’s best to send them over to Support and ask for assistance in further troubleshooting.

Sources: https://help.salesforce.com/articleView?id=000314336&type=1&mode=1, https://help.salesforce.com/articleView?id=000314349&language=en_US&type=1&mode=1, https://help.salesforce.com/articleView?id=mc_co_initiate_log.htm&type=5, https://help.salesforce.com/articleView?id=000320849&type=1&mode=1

Troubleshooting Marketing Cloud Connect

Marketing Cloud Connect integrates your Salesforce Marketing Cloud instance with Salesforce Sales, Service, and Community Clouds. It’s an easy-to-configure set of functionality that can bring great benefits to your organization. There is an official Marketing Cloud Connect troubleshooting guide available, but I’ve decided to share a few tips and tricks that usually solve most common connection problems.

To be able to correctly troubleshoot Marketing Cloud Connect, you will need the credentials of your system users, which are used to establish the connection between the clouds – your Salesforce System User (API User) and Marketing Cloud API User.

What is also very important, is to make sure that you are logged out of any active Marketing Cloud and Sales Cloud sessions – this is a rookie mistake, which leads to connecting both clouds using a “regular” user, instead of the dedicated Marketing Cloud API user. The best way to avoid making this mistake is to always use the Incognito Mode when working with Marketing Cloud Connect API users.

Change API user

You may receive an email from the Marketing Cloud Connect managed package with the subject: “Salesforce Marketing Cloud Connect – Action Required to Prevent Service Interruption.” This email would be sent if the package detects an issue when trying to connect your Salesforce org with your Marketing Cloud account through the Marketing Cloud Connect API User. To avoid interruption of service with Marketing Cloud Connect, reconnect your Marketing Cloud Connect API User:

  1. Log into your Sales or Service Cloud org as the System User (API User)
  2. Switch to Salesforce Classic interface
  3. Navigate to the Marketing Cloud tab
  4. Re-enter your Marketing Cloud API user credentials

It is possible, however, that the system won’t let you reconnect the API user and you will see the following error message:

Sorry, something went wrong.
We couldn’t retrieve the API User details. Try again or open a support case in the Help and Training portal.

In that case, follow these steps to reconnect your Salesforce System User (API User) without the need to delete the Connector Configuration:

  1. Login to Sales Cloud Org – as the System User (API User)
  2. Click Setup
  3. In the quick find search box type in ‘Visualforce Pages’
  4. Click on ‘Visualforce Pages
  5. Locate the label for ‘ChangeAPIUser
  6. Click on that label
  7. Click on the ‘Preview‘ button
  8. Click ‘Connect to Marketing Cloud‘ button

Source: https://help.salesforce.com/articleView?id=000318452&type=1&mode=1

Clearing out User Mappings and OAuth tokens

There are multiple scenarios where it’s necessary to remove the mapping between a Marketing Cloud User and a Salesforce User related to MC Connect, for example, if the user accidentally entered the wrong username/password when they first mapped their orgs; if permissions for a User on the Marketing Cloud side have changed; or if OAuth token is stored in Salesforce, but User is still prompted for Username and Password when selecting the Marketing Cloud Tab in Sales/Service Cloud.

Proceed with the following steps to clear OAuth tokens:

  1. Log in to Salesforce using an incognito (private) browser session.
  2. Select your Username in the upper right-hand corner, then select Developer Console.
  3. Click Debug, then select Execute anonymous window
  4. Execute the following Code, each line separately:
et4ae5.SupportUtilities.deleteUserTokens('Id_of_System_User');  
et4ae5.SupportUtilities.deleteUserTokens('Id_of_System_User|Parent_BU_MID');
et4ae5.SupportUtilities.deleteUserTokens('Id_of_System_User|Default_BU_MID'); 
et4ae5.SupportUtilities.deleteUserTokens('APIUSER');
et4ae5.SupportUtilities.deleteUserTokens('APIUSER|Parent_BU_MID');
et4ae5.SupportUtilities.deleteUserTokens('APIUSER|Default_BU_MID'); 

Note: Replace the Id_of_System_User in the code above with the Salesforce ID of your system user; replace MID with MID of the integrated Business Unit in Marketing Cloud. Use only MIDs relevant to your connection.

Afterwards, use the incognito window to access the ‘Marketing Cloud’ tab again. This should reconnect your user using the correct token. 

Source: https://help.salesforce.com/articleView?id=000336685&type=1&mode=1

Clearing Marketing Cloud Connect Configuration

If none of the above helped, you can try clearing the Marketing Cloud Connect configuration and re-configuring it from scratch.

As a first step, you will have to add the “Configurations” tab to your Sales/Service Cloud:

  1. Click Setup
  2. Follow the steps based on your UI:
    • Salesforce Classic: Under the “Create” category, select Tabs
    • Salesforce Lightning: Under the “User Interface” category, select Tabs
  3. Click New
  4. Choose “Configurations” from the Object drop-down menu
  5. Pick any Tab style
  6. Click Next > Next > Save

Once the tab has been added, navigate to it by clicking the Plus symbol on the tab bar and click on the Configurations link. Now delete the existing configuration:

  1. Choose All under the “View” drop-down, and then click Go
  2. Delete the existing Configuration Object

Now you are ready to re-configure Marketing Cloud Connect:

  1. Click the Marketing Cloud tab
  2. Enter the Marketing Cloud API Username and Password
  3. Select values for Send Types, Target Audience, Exclusion Audience, Support Ticket Recipient, and Tracking Preferences
  4. Click Marketing Cloud Tab once more, re-integrate individual Users as needed

Source: https://help.salesforce.com/articleView?id=000337425&type=1&mode=1