Solution Recipe 16: “Opt-Out Survey” — Capture and record an unsubscribe reason when customers opt-out
Rob
9min read
Developer recipes
January 3, 2023

Solution Recipes are tutorials to achieve specific objectives in Klaviyo. They can also help you master Klaviyo, learn new third-party technologies, and come up with creative ideas. They are written mainly for developers & technically-advanced users.

Note: We do our best to make sure any code and API references are accurate and current when this is published, but you might need to update code and it’s always a best practice to leverage our latest API versions. If you have questions, feel free to hop over to our Developer Community.

What you’ll learn

How to ask your subscribes for an “unsubscribe reason” when they opt-out from your emails

Why it matters

We often assume customers unsubscribe and opt out of emails simply because they are no longer interested in our brand. However this is not always the case, and the best time to ask someone is when they unsubscribing. This will help you improve your communication strategy and reduce your unsubscribe rate.

Level of sophistication

Intermediate

Prerequisite

In order to implement this solution recipe, you will need to be utilizing Klaviyo’s Hosted Pages feature for your accounts unsubscribe page.

Related Read: Solution Recipe 8: How to create a custom multilingual unsubscribe and preference page in Klaviyo

The Hosted Pages feature allows us to customise all of the code on the unsubscribe page. We can add our own HTML, CSS and most importantly for this solution recipe — Javascript.

Ingredients

  • Intermediate HTML and CSS formatting experience
  • A basic knowledge of the Django templating language
  • A working knowledge of the Klaviyo /track API
  • A heaped tablespoon of Javascript

Solution Design

There are a few moving parts to this solution recipe which are illustrated below.

Overview of how we capture the unsubscribe reason in Klaviyo

At a high level the following steps occur:

  • A user profile receives an email from Klaviyo, opens it and clicks on the “unsubscribe” link.
  • This user is then directed to a custom Klaviyo Hosted Unsubscribe page.
  • The user selects an unsubscribe reason from the form before submitting their unsubscribe request.
  • Upon submitting the unsubscribe request, an AJAX call is made to send an event to Klaviyo’s track API with the reason the user unsubscribed. The unsubscribe is also recorded separately as part of Klaviyo’s Hosted Page logic.

Once the unsubscribe reason events are recorded in Klaviyo, these can then be reported on using custom reports or segments in Klaviyo.

This is what the final unsubscribe page we are going to build will look like.

You are free to modify the look, feel and options as you wish. This is simply a template for you to get started with.

Instructions

Step 1: Enable Hosted Pages in your Klaviyo account

If you have not already, you will need to enable the Hosted Pages feature in your Klaviyo account.

Step 2: Create an unsubscribe page

For this example, we are going to use a standard boilerplate unsubscribe page. You are free to customise the look and feel of this page as you wish.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Unsubscribe page</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-md-6 offset-md-3 mt-3">
          <form action="" method="POST" class="card" >
              <!-- this hidden field tells Klaviyo what to do when the form is submitted -->
             <input type="hidden" name="$unsubscribe" value="true" />
             <!-- this hidden field tells Klaviyo where to redirect the user after they unsubscribe -->
            <!--<input type="hidden" name="$unsubscribed_url" value="/p/preferences_updated" />-->
            <div class="card-body">
              <div class="mb-3">
                <h3>We're sorry to see you go, please confirm your email below</h3>
                </div>
              <div class="mb-3">
                <label for="emailInput" class="form-label">Your email address</label>
                <input type="email" class="form-control" id="emailInput" name="$email" value="{{ person.email|default:'' }}" >
              </div>
           
                <button type="submit" class="btn btn-primary">Unsubscribe</button>
              </div>

            </div>
          </form>
        </div>
      </div>
    </div>
  </body>
</html>

Step 3: Add your unsubscribe/opt-out survey questions

Now that we have a basic unsubscribe page, we are going to add some radio buttons to allow a user to choose a reason why they are unsubscribing.

Additionally we will also add a field to allow a user to enter an “other” reason or give additional feedback.

<div class="mb-3 form-group radio-group">
   <p>Before you go – we’ve got a quick favour to ask. If you let us know why you’ve had enough, you’ll help us brush up our email communication.</p>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="Too_Frequent" id="Too_Frequent" />   
      <label class="form-check-label" for="Too_Frequent">Too many emails</label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="No_Longer_Relevant" id="No_Longer_Relevant"/>
      <label class="form-check-label" for="No_Longer_Relevant">I've lost interest</label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="Social_Media_Preference" id="Social_Media_Preference" />
      <label class="form-check-label" for="Social_Media_Preference">I get what I need from your socials</label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="Did_Not_Sign_Up" id="Did_Not_Sign_Up" />
      <label class="form-check-label" for="Did_Not_Sign_Up">I can’t remember signing up</label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="One_time_Offer" id="One_time_Offer"  />
      <label class="form-check-label" for="One_time_Offer">I signed up for an offer</label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="Redundant Email Address" id="Redundant_Email_Address" />
      <label class="form-check-label" for="Redundant_Email_Address">I use another email address </label>
   </div>
   <div class="form-check">
      <input class="form-check-input" type="radio" name="unsubscribeReason" value="Other" id="Other" {% if 'One-time Offer' in person.unsubscribeReason or 'Other' in request.POST.unsubscribeReason %}checked="checked" {% elif not person.unsubscribeReason and not request.POST.unsubscribeReason %}{% endif %} />
      <label class="form-check-label" for="Other">Other (fill us in below)</label>
   </div>
   <div class="form-group mt-3">
      <textarea id="otherUnsubreason" class="form-control" rows="3" placeholder="Please tell us why or any other comments you have"></textarea>
   </div>
</div>

Step 4: Send events to Klaviyo

The last piece of the puzzle is saving this information in Klaviyo. To do this, we are going to send an event Submitted Unsubscribe Reason which will record the unsubscribe reason and also add a profile property with the unsubscribe reason.

The snippet of code below, will capture the details of the form and send an event using Klaviyo’s track API as soon as the person clicks the “unsubscribe” button.

 <script>
      $(function () {
        try {
            $('button[type="submit"]').on('click', () => {
                var email = $('input[name="$email"]').val();
                var unsubReason = $('input[name="unsubscribeReason"]:checked').val();
                var unsubText = "";
                if (unsubReason == 'Other') {
                    unsubText = $('textarea#otherUnsubreason').val();
                }
                if (unsubReason != '') {
                  const options = {
                        method: 'POST',
                        headers: {
                          accept: 'application/json',
                          revision: '2024-06-15',
                          'content-type': 'application/json'
                        },
                        body: JSON.stringify({
                          data: {
                            type: 'event',
                            attributes: {
                              properties: { 'Unsubscribe Reason': unsubReason, 'Unsubscribe Other Reason': unsubText},
                              metric: {data: {type: 'metric', attributes: {name: 'Submitted Unsubscribe Reason'}}},
                              profile: {
                                data: {
                                  type: 'profile',
                                  attributes: {
                                    properties: {
                                        unsubscribeReason: unsubReason,
                                        unsubscribeOtherReason: unsubText
                                    },
                                    email
                                  }
                                }
                              }
                            }
                          }
                        })
                      };
                      
                      fetch('https://a.klaviyo.com/client/events/?company_id={{organization.id}}', options)
                        .then(response => response.json())
                        .then(response => console.log(response))
                        .catch(err => console.error(err));
                }
            });
        } catch (e) {
            console.log(e);
        }
      });

          $('input[name="unsubscribeReason"]').on("change", function () {
          $('form .form-actions button[type="submit"]').toggleClass("cancel", $(this).is(":checked"));
        });
        
    </script>

Putting it all together

The full hosted page code is below:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Unsubscribe page</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-md-6 offset-md-3 mt-3">
          <form action="" method="POST" class="card" >
              <!-- this hidden field tells Klaviyo what to do when the form is submitted -->
             <input type="hidden" name="$unsubscribe" value="true" />
             <!-- this hidden field tells Klaviyo where to redirect the user after they unsubscribe -->
            <!--<input type="hidden" name="$unsubscribed_url" value="/p/preferences_updated" />-->
            <div class="card-body">
              <div class="mb-3">
                <h3>We're sorry to see you go, please confirm your email below</h3>
                </div>
              <div class="mb-3">
                <label for="emailInput" class="form-label">Your email address</label>
                <input type="email" class="form-control" id="emailInput" name="$email" value="{{ person.email|default:'' }}" >
              </div>
              <div class="mb-3 form-group radio-group">
                  <p>Before you go – we’ve got a quick favour to ask. If you let us know why you’ve had enough, you’ll help us brush up our email communication.</p>
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="Too_Frequent" id="Too_Frequent" />   
                    <label class="form-check-label" for="Too_Frequent">Too many emails</label>
                  </div>
      
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="No_Longer_Relevant" id="No_Longer_Relevant"/>
                    <label class="form-check-label" for="No_Longer_Relevant">I've lost interest</label>
                  </div>
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="Social_Media_Preference" id="Social_Media_Preference" />
                    <label class="form-check-label" for="Social_Media_Preference">I get what I need from your socials</label>
                  </div>
                  <div class="form-check">
                     <input class="form-check-input" type="radio" name="unsubscribeReason" value="Did_Not_Sign_Up" id="Did_Not_Sign_Up" />
                     <label class="form-check-label" for="Did_Not_Sign_Up">I can’t remember signing up</label>
                  </div>
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="One_time_Offer" id="One_time_Offer"  />
                    <label class="form-check-label" for="One_time_Offer">I signed up for an offer</label>
                  </div>
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="Redundant Email Address" id="Redundant_Email_Address" />
                    <label class="form-check-label" for="Redundant_Email_Address">I use another email address </label>
                  </div>
                   <div class="form-check">
                    <input class="form-check-input" type="radio" name="unsubscribeReason" value="Other" id="Other" {% if 'One-time Offer' in person.unsubscribeReason or 'Other' in request.POST.unsubscribeReason %}checked="checked" {% elif not person.unsubscribeReason and not request.POST.unsubscribeReason %}{% endif %} />
                    <label class="form-check-label" for="Other">Other (fill us in below)</label>
                  </div>
                  <div class="form-group mt-3">
                      <textarea id="otherUnsubreason" class="form-control" rows="3" placeholder="Please tell us why or any other comments you have"></textarea>
                  </div>
                </div>
                <button type="submit" class="btn btn-primary">Unsubscribe</button>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

   <script>
      $(function () {
        try {
            $('button[type="submit"]').on('click', () => {
                var email = $('input[name="$email"]').val();
                var unsubReason = $('input[name="unsubscribeReason"]:checked').val();
                var unsubText = "";
                if (unsubReason == 'Other') {
                    unsubText = $('textarea#otherUnsubreason').val();
                }
                if (unsubReason != '') {
                      const options = {
                        method: 'POST',
                        headers: {
                          accept: 'application/json',
                          revision: '2024-06-15',
                          'content-type': 'application/json'
                        },
                        body: JSON.stringify({
                          data: {
                            type: 'event',
                            attributes: {
                              properties: { 'Unsubscribe Reason': unsubReason, 'Unsubscribe Other Reason': unsubText},
                              metric: {data: {type: 'metric', attributes: {name: 'Submitted Unsubscribe Reason'}}},
                              profile: {
                                data: {
                                  type: 'profile',
                                  attributes: {
                                    properties: {
                                        unsubscribeReason: unsubReason,
                                        unsubscribeOtherReason: unsubText
                                    },
                                    email
                                  }
                                }
                              }
                            }
                          }
                        })
                      };
                      
                      fetch('https://a.klaviyo.com/client/events/?company_id={{organization.id}}', options)
                        .then(response => response.json())
                        .then(response => console.log(response))
                        .catch(err => console.error(err));
                }
            });
        } catch (e) {
            console.log(e);
        }
      });

          $('input[name="unsubscribeReason"]').on("change", function () {
          $('form .form-actions button[type="submit"]').toggleClass("cancel", $(this).is(":checked"));
        });
        
    </script>
  </body>
</html>

Configuring your unsubscribe page

Don’t forget, in order for this unsubscribe page to actually be used in your emails, you will need to update your Klaviyo consent page settings to use the custom hosted pages. Instructions on how to do this can be found here.

Segment/Report on your opt-out survey

Once you have activated this hosted page on your account, you will start to see unsubscribe events.

And the profiles will also have properties added to them as well.

Using this information we can now create segments or reports to get an understanding of why people are unsubscribing:

An example of how to build a segment based on unsubscribe reason
An example of how to build a report of unsubscribe reasons

Impact

Now you are able to gather more detailed information as to why users are unsubscribing. This will allow you to better tailor your marketing campaign and strategy to help you reduce your unsubscribe rates and ensure you are sending relevant content to your customers.

Rob
Rob Boland