Double opt-in
Marketing Cloud

Part-2 Custom approach to implementing Double Opt-in in the Marketing Cloud

Double Opt-in

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).

MarketingCloud

Pre-requisites:

  • Make sure that your Marketing Cloud account is integrated with Salesforce CRM.
  • Create two fields on Salesforce CRM Object say for example-
    1. Verified – to check if the email is verified or not. If checked = true then verified else not verified.
    2. 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:

  1. The customer will register using the Signup form.

    MarketingCloud

  2. 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.
  3. 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) 
    ]%%
  4. 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() 
    ]%%
  5. 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  
    ]%%
    
  6. 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!")) 
    ]%%
    
  7. 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 
    ]%%
    
  8. 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.

    MarketingCloud

    
    <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>
    
  9. 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.

    MarketingCloud

  10. 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.

    MarketingCloud

  11. 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:

    MarketingCloud

    Data Extension structure for this use case:

    MarketingCloud

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.

Leave a Reply