In the previous blog, Part-1 Custom Approach to Implement Double Opt-In in the Marketing Cloud we have discussed Solution Approach 1 Implement double opt-in with Email Address being subscriber key (assuming SFMC is not integrated with Salesforce CRM) . In this blog, we are implementing Solution Approach 2 Use Double opt-in with Salesforce CRM being a system of record and integrated with SFMC
Use Case:
I was in the middle of an SFMC implementation for a very large retail client sending up to 500k emails a day, deliverability and inbox placement were of utmost importance as they were direct revenue drivers. The client had a great signup volume on the website but was struggling with bounces on the first email sent to customers as they ended up capturing the wrong email address added by users to avoid signup, which impacted the overall deliverability for the client. The client was a bit shy of introducing another integration that checks the email addresses on the form as this just didn’t involve extra cost but was not super reliable. So, we agreed upon implementing a double opt-in solution with some twist and customization that I will walk through during this blog, brace yourself for a lengthy blog.😊
Solution Approach:
We provided them the solution to have double opt-in from the subscribers so that their email address is verified from the email sent to the valid email that they use. This also ensures that the subscriber is interested in receiving emails from you and since it’s a user-driven confirmation chances of the email address being wrong is Zero.
Approach 2:
Use Double opt-in with Salesforce CRM being a system of record and integrated with SFMC
If Marketing Cloud is integrated with Salesforce CRM and Contact Id is the SubscriberKey, the flow will look something like as shown in the below flow chart.
We are using Amp script, Journey (to send verification email and reminder email to verify the email address if the subscriber does not verify the email for the first time), and SSJS (to trigger the API event).
Pre-requisites:
- Make sure that your Marketing Cloud account is integrated with Salesforce CRM.
- Create two fields on Salesforce CRM Object say for example-
- Verified – to check if the email is verified or not. If checked = true then verified else not verified.
- Signup Date – This will store the current date when the customer Signs up using the Sign-Up form.
- Make sure the duplicate rule allows only one email address to be created using the flow on the CRM.
- Create a data extension that will store the results from the signup form with fields like firstname, lastname, signupdate, email address, etc. as per the requirement.
- Also, a journey will be created with an API event as an entry source. Details of the journey are listed below in this blog.
Steps:
- The customer will register using the Signup form.
- The customer fills the form by manually entering the details such as FirstName, LastName, EmailAddress, and other details that are there in the signup form.
- Using the email address field, the record is searched on CRM.
When the customer clicks on the Submit button, a call to CRM is made using the RetrieveSalesforceObject to check if the email address entered by the customer already exists on Salesforce CRM.%%[ IF RequestParameter("submitted") == "Submit" then set @fname = RequestParameter('FirstName') set @lname = RequestParameter('lastname') set @email = RequestParameter('email') set @subscriberRows = RetrieveSalesforceObjects("Contact", "FirstName, LastName, Email, Verified__c, Id, HasOptedOutOfEmail ", "Email", "=", @email) ]%%
- The decision in this step checks if the email on the signup form entered by the customer matches the email address on the CRM. If the email address matches i.e., if there is one record then fetch the values of that record.
%%[ IF RowCount(@subscriberRows) == 1 THEN set @subscriberRow = Row(@subscriberRows, 1) set @firstName = Field(@subscriberRow, "FirstName") set @lastName = Field(@subscriberRow, "LastName") set @emailaddress = Field(@subscriberRow, "Email") set @verified = Field(@subscriberRow, "Verified__c") set @Id = Field(@subscriberRow, "Id") set @hasoptout = Field(@subscriberRow, "HasOptedOutOfEmail") set @emailoptout = false set @date1 = NOW() ]%%
- Another decision checks
If the email found in Step 2 is already verified or not. If it is verified then just update the field’s first name, and last name, set ‘HasOptedOutOfEmail’ to false, and signup date to the current date.
Note: You can update any field as per the requirement.%%[ IF (@verified == true) THEN set @UpdateRecord = UpdateSingleSalesforceObject('Contact', @Id, 'FirstName', @fname,'LastName', @lname, 'HasOptedOutOfEmail', @emailoptout, 'signup_text__c', @date1) Output(Concat("Record already exist and verified!")) set @journeyFlag = 0 ]%%
- If the email is not verified, then send a verification email using the journey builder API event and update the updated details of the customer on CRM as per the details entered.
%%[ set @UpdateRecord = UpdateSingleSalesforceObject('Contact', @Id, 'FirstName', @fname,'LastName', @lname, 'HasOptedOutOfEmail', @emailoptout, 'signup_text__c', @date1) set @journeyFlag = 1 Output (Concat("Record already exist and not verified!")) ]%%
- If there is no row found in Step 4 then create a new contact on CRM with the details from the Sign-Up form.
%%[ set @createrecord = CreateSalesforceObject("Contact", 3, "FirstName", @fname, "LastName", @lname, "Email", @email) set @Id = @createrecord set @verified = false set @journeyFlag = 1 ]%%
- Then, send a verification email to verify the email address using the journey builder API event. If the subscriber verifies the email, then update the CRM with verified as true and exit the contact from the journey. No reminder will be sent to the subscriber.
<script runat="server"> var headerNames = ["Authorization"]; var headerValues = ["Bearer " + accessToken]; var jsonBody = { "ContactKey": id, "EventDefinitionKey": 'APIEvent-fe26d2f8-214e-1962-7316-502caca311b2', "Data": { "FirstName" : firstName, "LastName": lastName, "Email": emailaddress, "Id": id, "Verified" : verified, "HasOptedOutOfEmail" : HasOptedOutOfEmail, "SignUpDate" : SignUpDate } } var requestUrl = rest_instance_url + "interaction/v1/events"; var fireEntryEvent = HTTP.Post(requestUrl, contentType, Stringify(jsonBody), headerNames, headerValues); </script>
- When the subscriber clicks on the verify your email address button on the email message, a message will be displayed and the system of records that is Salesforce CRM field Verified is updated with true value and exits from the journey.
- If the email is not verified i.e., the subscriber does not click on the verification link in the email, then send a reminder email to verify the email address.
- If the subscriber verifies the reminder email, then set verified equals to true on CRM and exit the contact from the journey.
Journey Structure for this use case:Data Extension structure for this use case:
Full code for Approach 2
/* Ampscript to retrieve salesforce contact object and based on the conditions create or update the salesforce CRM object records */
%%[
var @fname, @lname, @email, @rows, @rowcount, @subscriberRow, @firstName, @lastName, @emailaddress, @verified, @UpdateRecord, @createrecord, @hasoptout, @Id, @journeyFlag
/*Storing the values in variables after the customer submits the form*/
IF RequestParameter("submitted") == "Submit" then
set @fname = RequestParameter('FirstName')
set @lname = RequestParameter('lastname')
set @email = RequestParameter('email')
/* Retrieving the row from Salesforce CRM if exists*/
set @subscriberRows = RetrieveSalesforceObjects("Contact", "FirstName, LastName, Email, Verified__c, Id, HasOptedOutOfEmail ", "Email", "=", @email)
IF RowCount(@subscriberRows) == 1 THEN
set @subscriberRow = Row(@subscriberRows, 1)
set @firstName = Field(@subscriberRow, "FirstName")
set @lastName = Field(@subscriberRow, "LastName")
set @emailaddress = Field(@subscriberRow, "Email")
set @verified = Field(@subscriberRow, "Verified__c")
set @Id = Field(@subscriberRow, "Id")
set @hasoptout = Field(@subscriberRow, "HasOptedOutOfEmail")
set @emailoptout = false
set @date1 = NOW()
/* Updating contact object on CRM as per the requirement*/
IF (@verified == true) THEN
set @UpdateRecord = UpdateSingleSalesforceObject('Contact', @Id, 'FirstName', @fname,'LastName', @lname, 'HasOptedOutOfEmail', @emailoptout, 'signup_text__c', @date1)
Output(Concat("Record already exist and verified!"))
set @journeyFlag = 0 //API will not trigger based on this flag if it is set as false
ELSE
set @UpdateRecord = UpdateSingleSalesforceObject('Contact', @Id, 'FirstName', @fname,'LastName', @lname, 'HasOptedOutOfEmail', @emailoptout, 'signup_text__c', @date1)
set @journeyFlag = 1 //API will trigger based on this flag if it is set as true
Output(Concat("Record already exist and not verified!"))
ENDIF
ELSE
/*Creating a record in Salesforce CRM if there is no record on CRM based on the email address*/
set @createrecord = CreateSalesforceObject("Contact", 3, "FirstName", @fname, "LastName", @lname, "Email", @email)
set @Id = @createrecord
set @verified = false
set @journeyFlag = 1
Output(Concat("Your record has been added successfully!"))
ENDIF
ENDIF
]%%
<script runat="server">
Platform.Load("Core","1");
try
{
var jFlag = Variable.GetValue("@journeyFlag"); //Getting flag value from AmpScript
if(jFlag == 1) // Checking flag set as true, then set payload to generate token
{
var authEndpoint = "https://mc-xxxxxxxxxxxxxxx-xxxxxxxxx.auth.marketingcloudapis.com/";
var payload = {
account_id: "514xxxxxx",
client_id: "tc0dsxxxxxxxxxxxxxxxxxxx",
client_secret: "OF3xxxxxxxxxxxxxxxxxxxxx",
grant_type: "client_credentials"
};
var url = authEndpoint + 'v2/token' ;
var contentType = 'application/json';
var accessTokenRequest = HTTP.Post(url, contentType, Stringify(payload));
/* Checking generated Token Status as 200, then set variables to get form values and send to Journey API Event */
if (accessTokenRequest.StatusCode == 200)
{
var tokenResponse = Platform.Function.ParseJSON(accessTokenRequest.Response[0]);
var accessToken = tokenResponse.access_token
var rest_instance_url = tokenResponse.rest_instance_url
}
var emailaddress = Variable.GetValue("@email");
var verified = Variable.GetValue("@verified");
if (emailaddress != null && accessToken != null && verified != true)
{
var firstName = Variable.GetValue("@fname");
var lastName = Variable.GetValue("@lname");
var id = Variable.GetValue("@Id");
var headerNames = ["Authorization"];
var headerValues = ["Bearer " + accessToken];
var jsonBody =
{
"ContactKey": id,
/* Journey API Event Definition Key */
"EventDefinitionKey": 'APIEvent-fexxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
"Data":
{
"FirstName" : firstName,
"LastName": lastName,
"Email": emailaddress,
"Id": id
}
}
var requestUrl = rest_instance_url + "interaction/v1/events";
var fireEntryEvent = HTTP.Post(requestUrl, contentType, Stringify(jsonBody), headerNames, headerValues);
if (fireEntryEvent.StatusCode == 201 )
{
Write("Form Submitted Successfully!")
}
else
{
Write("Form Not Submitted");
}
}
}
}
catch (err)
{
Write("Error Message: " + Stringify(err.message)+"<br> Description: "+ Stringify(err.description));
}
</script>
Pros of Double Opt-in
- It improves deliverability as you always have verified email addresses and you are less likely to receive spam complaints from users receiving your emails.
- Your emails are less likely to bounce and your email open rates are likely to be higher.
- This method ensures that you have permission and verification from users that they want to receive your emails and helps to avoid any legal issues, thus increasing the engagement from the customers.
Cons of Double Opt-in
- Your email lists will take a bit longer to grow as there is one extra step involved in this process. The customers who have signed up using the signup form have to check their inbox to confirm their email.
- It leads to the loss of potential subscribers as some people don’t bother to open the mail and verify the email and some may even forget to click on the confirmation link.
- Loss of user attention: Users who sign up via double opt-in expect more, so if you don’t provide engaging content consistently, you’re more likely to lose their attention.