---
title: Implementing newsletter subscriptions
related:
  - https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/preparing-email-templates.md
  - https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/email-marketing/creating-newsletters.md
  - https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/enabling-marketing-email-tracking.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

When using the Xperience [email marketing](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/email-marketing.md) features, you manage and send [newsletters](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/email-marketing/creating-newsletters.md) from the administration interface of the Xperience application. All settings related to email marketing that you configure in Xperience apply (both global settings and the configuration of individual newsletters). On the side of the MVC application, you need to handle the actions that visitors perform on the pages of the website – newsletter subscription and unsubscription.

To allow visitors on your website to manage subscriptions for your newsletters:

1. [Initialize subscription-related services](#initializing-subscription-services).
2. Implement the required functionality on your MVC site:

   - [Subscription](#setting-up-subscription)
   - [Unsubscription](#setting-up-unsubscription)
   - [Processing of subscription confirmations](#handling-confirmations-for-double-opt-in-subscription) (if your newsletters use [double opt-in](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/enabling-double-opt-in-for-newsletters.md))

## Initializing subscription services

The API for managing newsletter subscriptions is provided by services from the **CMS.Newsletters** namespace (part of the API provided by the _Kentico.Xperience.Libraries_ NuGet package).

- **ISubscriptionService** – handles subscription and unsubscription for email feeds.
- **IUnsubscriptionProvider** – allows verification and management of email feed unsubscriptions and opt-out lists.
- **IContactProvider** – creates or gets contact objects, which are used to represent newsletter recipients (subscribers).
- **IEmailHashValidator** – handles validation of security hashes for email addresses (for unsubscription and subscription confirmation links).
- **ISubscriptionApprovalService** – handles approval of subscriptions for newsletters that use [double opt-in](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/enabling-double-opt-in-for-newsletters.md).

You need to initialize instances of these services and make them available in the related controllers.

> **Tip:** We recommend using a [dependency injection container](https://docs.kentico.com/13/developing-websites/initializing-xperience-services-with-dependency-injection.md) to initialize service instances.

```csharp

using CMS.Newsletters;
using CMS.Core;


```

```csharp

        private readonly ISubscriptionService subscriptionService;
        private readonly IUnsubscriptionProvider unsubscriptionProvider;
        private readonly IContactProvider contactProvider;
        private readonly IEmailHashValidator emailHashValidator;
        private readonly ISubscriptionApprovalService subscriptionApprovalService;
        private readonly IIssueInfoProvider issueInfoProvider;
        private readonly INewsletterInfoProvider newsletterInfoProvider;
        private readonly ISiteService siteService;

        public NewsletterSubscriptionController(ISubscriptionService subscriptionService,
                                                IUnsubscriptionProvider unsubscriptionProvider,
                                                IContactProvider contactProvider,
                                                IEmailHashValidator emailHashValidator,
                                                ISubscriptionApprovalService subscriptionApprovalService,
                                                IIssueInfoProvider issueInfoProvider,
                                                INewsletterInfoProvider newsletterInfoProvider,
                                                ISiteService siteService)
        {
            this.subscriptionService = subscriptionService;
            this.unsubscriptionProvider = unsubscriptionProvider;
            this.contactProvider = contactProvider;
            this.emailHashValidator = emailHashValidator;
            this.subscriptionApprovalService = subscriptionApprovalService;
            this.issueInfoProvider = issueInfoProvider;
            this.newsletterInfoProvider = newsletterInfoProvider;
            this.siteService = siteService;
        }


```

You can now call the methods of the subscription services within your application's code to perform actions related to newsletter subscription and unsubscription.

## Setting up subscription

Use the following approach to develop actions that allow visitors to subscribe to newsletters on your website:

> **Tip:** **Tip**: To view the full code of a functional example, you can inspect and download the [LearningKit project](https://github.com/Kentico/LearningKit-Mvc) on GitHub. You can also run the LearningKit website by connecting the project to an Xperience database.

1. Create a newsletter for your site in Xperience (or use an existing one). See [Creating newsletters](https://docs.kentico.com/13/on-line-marketing-features/managing-your-on-line-marketing-features/email-marketing/creating-newsletters.md) for more information.

2. Create a new controller class in your MVC project or edit an existing one.

3. Implement two subscription actions – one basic GET action to display a subscription form and a second POST action to handle the creation of new recipients when the form is submitted.

4. Perform the following steps within the POST action:

   1. Get a _ContactInfo_ object representing the new recipient based on the email address posted from the subscription form – call the **GetContactForSubscribing** method of the **IContactProvider** [service instance](#initializing-subscription-services).
   2. Use the **INewsletterInfoProvider** service to get a _NewsletterInfo_ object representing the newsletter for which you are implementing subscription.
   3. Call the **Subscribe** method of the **ISubscriptionService** instance with the prepared _ContactInfo_ and _NewsletterInfo_ objects as parameters.

      > **Info:** **Notes**:
      >
      > - The **GetContactForSubscribing** method automatically creates the contact object in Xperience based on the specified email address (if necessary).
      > - The **Subscribe** method adds the contact as a recipient of the newsletter.
      > - If you wish to check whether a contact with the given email address is already a recipient of a newsletter, call the **IsSubscribed** method of the **ISubscriptionService** instance.

      ```csharp

      using System;
      using System.Web.Mvc;

      using CMS.ContactManagement;
      using CMS.Newsletters;
      using CMS.SiteProvider;
      using CMS.Helpers;
      using CMS.Base;


      ```

      ```csharp title="Subscription actions example"

              /// <summary>
              /// Basic action that displays the newsletter subscription form.
              /// </summary>
              public ActionResult Subscribe()
              {
                  return View();
              }

              /// <summary>
              /// Handles creation of new marketing email recipients when the subscription form is submitted.
              /// Accepts an email address parameter posted from the subscription form.
              /// </summary>
              [HttpPost]
              [ValidateAntiForgeryToken]
              public ActionResult Subscribe(NewsletterSubscribeViewModel model)
              {
                  if (!ModelState.IsValid)
                  {
                      // If the subscription data is not valid, displays the subscription form with error messages
                      return View(model);
                  }

                  // Either gets an existing contact by email or creates a new contact object with the given email
                  ContactInfo contact = contactProvider.GetContactForSubscribing(model.Email);

                  // Gets a newsletter
                  // Fill in the code name of your newsletter object in Xperience
                  NewsletterInfo newsletter = newsletterInfoProvider.Get("SampleNewsletter", siteService.CurrentSite.SiteID);

                  // Prepares settings that configure the subscription behavior
                  var subscriptionSettings = new SubscribeSettings()
                  {
                      RemoveAlsoUnsubscriptionFromAllNewsletters = true, // Subscription removes the given email address from the global opt-out list for all marketing emails (if present)
                      SendConfirmationEmail = true, // Allows sending of confirmation emails for the subscription
                      AllowOptIn = true // Allows handling of double opt-in subscription for newsletters that have it enabled
                  };

                  // Subscribes the contact with the specified email address to the given newsletter
                  subscriptionService.Subscribe(contact, newsletter, subscriptionSettings);

                  // Passes information to the view, indicating whether the newsletter requires double opt-in for subscription
                  model.RequireDoubleOptIn = newsletter.NewsletterEnableOptIn;

                  // Displays a view to inform the user that the subscription was successful
                  return View("SubscribeSuccess", model);
              }


      ```

5. We recommend creating a view model for your subscription action _(NewsletterSubscribeViewModel_ in the example above). The view model allows you to:
   - Pass parameters from the subscription form (email address and, optionally, the recipient's name).
   - Use data annotations to define validation and formatting rules for the subscription data. See [System.ComponentModel.DataAnnotations](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations?view=netframework-4.8) for more information about the available data annotation attributes.
   - Pass information back to the subscription form, for example, a flag indicating that the newsletter requires a double opt-in subscription.

     ```csharp title="Subscription view model example"

     using System.ComponentModel;
     using System.ComponentModel.DataAnnotations;

     public class NewsletterSubscribeViewModel
     {
         [DataType(DataType.EmailAddress)]
         [Required(ErrorMessage = "The Email address cannot be empty.")]
         [DisplayName("Email address")]
         [EmailAddress(ErrorMessage = "Invalid email address.")]
         [MaxLength(254, ErrorMessage = "The Email address cannot be longer than 254 characters.")]
         public string Email
         {
             get;
             set;
         }

         /// <summary>
         /// Indicates whether the newsletter requires double-opt in for subscription.
         /// Allows the view to display appropriate information to newly subscribed users.
         /// </summary>
         [Bindable(false)]
         public bool RequireDoubleOptIn
         {
             get;
             set;
         }
     }

     ```

6. Design the user interface required for newsletter subscription on your website:
   - Create a view for the _Subscribe_ action and display an appropriate subscription form. We recommend using a strongly typed view based on your subscription view model.
   - Create any other views required by the _Subscribe_ action, for example, to inform users about successful subscriptions.

Your website's visitors can now subscribe to newsletters. The system automatically performs all standard Xperience subscription features, such as sending of confirmation emails based on the newsletter's _Subscription confirmation_ template.

Continue by setting up:

- [Handling of unsubscription requests](#setting-up-unsubscription)
- [Processing of subscription confirmations](#handling-confirmations-for-double-opt-in-subscription) (if your newsletters use double opt-in)

## Setting up unsubscription

Users can unsubscribe from email campaigns or newsletters by clicking links placed into the content of your emails.

When setting up unsubscription functionality, you first need to ensure that the Xperience application generates correct unsubscription links:

1. Add unsubscription links into the content of your marketing emails or templates (in the **Email marketing** application).

   > **Info:** Use the _{% EmailFeed.UnsubscribeFromEmailFeedUrl %}_ and _{% EmailFeed.UnsubscribeFromAllEmailFeedsUrl %}_ macros to generate the URLs of the links.
   >
   > See [Preparing email templates](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/preparing-email-templates.md) to learn more.
2. In the **Email marketing** application, edit your email feeds, open their **Configuration** tab and set the following properties:

   - **Base URL** – the base URL of your live site. Leave empty to use the site's _Presentation URL_. For example:  _https://www.SiteDomain.com_
   - **Unsubscription page URL** – must match the route of the action that handles unsubscription requests in your MVC application. For example: _\~/NewsletterSubscription/Unsubscribe_

     > **Tip:** To use a shared _Unsubscription page URL_ for multiple email feeds, leave the property empty for individual email feeds and set the value in the **Settings -> On-line marketing -> Email marketing -> Unsubscription page URL** setting.

Continue by implementing a controller action within your MVC application for handling of unsubscription requests:

1. Create a new model class in your MVC project. The model will represent the parameters that Xperience generates in newsletter and email campaign unsubscription links.

   ```csharp title="Unsubscription model"

   using System;
   using System.ComponentModel.DataAnnotations;

   public class MarketingEmailUnsubscribeModel
   {
       /// <summary>
       /// The email address of the recipient who is requesting unsubscription.
       /// </summary>
       [Required]
       public string Email
       {
           get;
           set;
       }

       /// <summary>
       /// The GUID (identifier) of the Xperience email feed related to the unsubscription request.
       /// </summary>
       [Required]
       public Guid NewsletterGuid
       {
           get;
           set;
       }

       /// <summary>
       /// The GUID (identifier) of the Xperience marketing email related to the unsubscription request.
       /// </summary>
       [Required]
       public Guid IssueGuid
       {
           get;
           set;
       }

       /// <summary>
       /// Hash for protection against forged unsubscription requests.
       /// </summary>
       [Required]
       public string Hash
       {
           get;
           set;
       }

       /// <summary>
       /// Indicates whether the unsubscription request is for all marketing emails or only a specific email feed.
       /// </summary>
       public bool UnsubscribeFromAll
       {
           get;
           set;
       }
   }

   ```

2. Add a GET action to your newsletter subscription controller and perform the following steps:

   1. Use the unsubscription model to pass parameters from the unsubscription request.
   2. Verify that the unsubscription hash is valid for the given email address – call the **ValidateEmailHash** method of the **IEmailHashValidator** [service instance](#initializing-subscription-services), with the email address and hash from the unsubscription request as parameters.
   3. Verify that the email address is not already unsubscribed by calling the **IsUnsubscribedFromSingleNewsletter** and **IsUnsubscribedFromAllNewsletters** methods of the **IUnsubscriptionProvider** service. Branch the logic based on the _UnsubscribeFromAll_ parameter from the unsubscription request.
   4. To remove subscriptions, call the **UnsubscribeFromSingleNewsletter** or **UnsubscribeFromAllNewsletters** method of the **ISubscriptionService** instance (with parameters taken from the unsubscription request).

      ```csharp

      using System.Web.Mvc;

      using CMS.Newsletters;
      using CMS.SiteProvider;

      ```

      ```csharp title="Unsubscription action example"

              /// <summary>
              /// Handles marketing email unsubscription requests.
              /// </summary>
              public ActionResult Unsubscribe(MarketingEmailUnsubscribeModel model)
              {
                  // Verifies that the unsubscription request contains all required parameters
                  if (ModelState.IsValid)
                  {
                      // Confirms whether the hash in the unsubscription request is valid for the given email address
                      // Provides protection against forged unsubscription requests
                      if (emailHashValidator.ValidateEmailHash(model.Hash, model.Email))
                      {
                          // Gets the marketing email (issue) from which the unsubscription request was sent
                          IssueInfo issue = issueInfoProvider.Get(model.IssueGuid, siteService.CurrentSite.SiteID);

                          if (model.UnsubscribeFromAll)
                          {
                              // Checks that the email address is not already unsubscribed from all marketing emails
                              if (!unsubscriptionProvider.IsUnsubscribedFromAllNewsletters(model.Email))
                              {
                                  // Unsubscribes the specified email address from all marketing emails (adds it to the opt-out list)
                                  subscriptionService.UnsubscribeFromAllNewsletters(model.Email, issue?.IssueID);
                              }
                          }
                          else
                          {
                              // Gets the email feed for which the unsubscription was requested
                              NewsletterInfo newsletter = newsletterInfoProvider.Get(model.NewsletterGuid, siteService.CurrentSite.SiteID);

                              if (newsletter != null)
                              {
                                  // Checks that the email address is not already unsubscribed from the specified email feed
                                  if (!unsubscriptionProvider.IsUnsubscribedFromSingleNewsletter(model.Email, newsletter.NewsletterID))
                                  {
                                      // Unsubscribes the specified email address from the email feed
                                      subscriptionService.UnsubscribeFromSingleNewsletter(model.Email, newsletter.NewsletterID, issue?.IssueID);
                                  }
                              }
                          }

                          // Displays a view to inform the user that they were unsubscribed
                          return View("UnsubscribeSuccess");
                      }
                  }

                  // If the unsubscription was not successful, displays a view to inform the user
                  // Failure can occur if the request does not provide all required parameters or if the hash is invalid
                  return View("UnsubscribeFailure");
              }


      ```

3. Create views for informing recipients about the result of their unsubscription requests.

When a user clicks on an unsubscription link in a newsletter, the URL leads to the unsubscribe action on your MVC site. The action validates the parameters and either unsubscribes the recipient or displays information if the request is invalid.

The content of the unsubscription page depends on your implementation of the returned views.

## Handling confirmations for double opt-in subscription

If your newsletters are configured to use [double opt-in](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/enabling-double-opt-in-for-newsletters.md), you need to set up additional logic on your MVC site to allow users to confirm their newsletter subscriptions.

When a visitor subscribes to a newsletter with double opt-in enabled, the system automatically adds the recipient with the "Waiting for confirmation" subscription status and the system sends a confirmation email to the given email address. You need to ensure that the double opt-in email contains a correct confirmation link:

1. Add the confirmation link into the content of the **Double opt-in template** assigned to your newsletter (in the **Email marketing** application).

   > **Info:** Use the _{% EmailFeed.SubscriptionConfirmationUrl %}_ macro to generate the URL of the subscription confirmation link. See [Preparing email templates](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/preparing-email-templates.md) to learn more.
2. In the **Email marketing** application, edit your newsletters, open their **Configuration** tab and set the following properties:
   - **Base URL** – the base URL of your live site. Leave empty to use the site's _Presentation URL_. For example:  _https://www.SiteDomain.com_
   - **Approval page URL** – must match the route of the action that handles subscription confirmation requests in your MVC application. For example: _\~/NewsletterSubscription/ConfirmSubscription_

> **Tip:** To use a shared _Approval page URL_ for multiple newsletters, leave the property empty for individual newsletters and set the value in the **Settings -> On-line marketing -> Email marketing -> Double opt-in approval page URL** setting.

Continue by implementing a controller action within your MVC application to handle the subscription confirmation requests:

1. Create a new model class in your MVC project. The model will represent the parameters that Xperience generates in subscription confirmation links.

   ```csharp title="Subscription confirmation view model"

   using System.ComponentModel.DataAnnotations;

   public class NewsletterConfirmSubscriptionViewModel
   {
       /// <summary>
       /// Hash used to identify the subscription and for protection against forged confirmation requests.
       /// </summary>
       [Required]
       public string SubscriptionHash
       {
           get;
           set;
       }

       /// <summary>
       /// The date and time of the original subscription. Used to detect expired confirmation requests.
       /// </summary>
       public string DateTime
       {
           get;
           set;
       }
   }

   ```

2. Add a GET action to your newsletter subscription controller and perform the following steps:

   1. Use the subscription confirmation model to pass parameters from the confirmation request.
   2. Parse the date and time parameter from the confirmation request. The value uses a specific date and time format required by the Xperience API (see the code example below for details).
   3. To confirm subscriptions, call the **ApproveSubscription** method of the **ISubscriptionApprovalService** [instance](#initializing-subscription-services) (with parameters taken from the hash and DateTime value in the confirmation request).
   4. Handle the result of the subscription confirmation based on the **ApprovalResult** value returned by the _ApproveSubscription_ method (see the code example below for information about the possible values).

      ```csharp

      using System;
      using System.Web.Mvc;

      using CMS.ContactManagement;
      using CMS.Newsletters;
      using CMS.SiteProvider;
      using CMS.Helpers;
      using CMS.Base;


      ```

      ```csharp title="Subscription confirmation action example"

              /// <summary>
              /// Handles confirmation requests for newsletter subscriptions (when using double opt-in).
              /// </summary>
              public ActionResult ConfirmSubscription(NewsletterConfirmSubscriptionViewModel model)
              {
                  // Verifies that the confirmation request contains the required hash parameter
                  if (!ModelState.IsValid)
                  {
                      // If the hash is missing, returns a view informing the user that the subscription confirmation was not successful
                      ModelState.AddModelError(String.Empty, "The confirmation link is invalid.");
                      return View(model);
                  }

                  // Attempts to parse the date and time parameter from the request query string
                  // Uses the date and time formats required by the Xperience API
                  DateTime parsedDateTime = DateTimeHelper.ZERO_TIME;
                  if (!string.IsNullOrEmpty(model.DateTime) && !DateTimeUrlFormatter.TryParse(model.DateTime, out parsedDateTime))
                  {
                      // Returns a view informing the user that the subscription confirmation was not successful
                      ModelState.AddModelError(String.Empty, "The confirmation link is invalid.");
                      return View(model);
                  }

                  // Attempts to confirm the subscription specified by the request's parameters
                  ApprovalResult result = subscriptionApprovalService.ApproveSubscription(model.SubscriptionHash, false, siteService.CurrentSite.SiteName, parsedDateTime);

                  switch (result)
                  {
                      // The confirmation was successful or the recipient was already approved
                      // Displays a view informing the user that the subscription is active
                      case ApprovalResult.Success:
                      case ApprovalResult.AlreadyApproved:
                          return View(model);

                      // The confirmation link has expired
                      // Expiration occurs after a certain number of hours from the time of the original subscription
                      // You can set the expiration interval in Xperience (Settings -> On‑line marketing -> Email marketing -> Double opt-in interval)
                      case ApprovalResult.TimeExceeded:
                          ModelState.AddModelError(String.Empty, "Your confirmation link has expired. Please subscribe to the newsletter again.");
                          break;

                      // The subscription specified in the request's parameters does not exist
                      case ApprovalResult.NotFound:
                          ModelState.AddModelError(String.Empty, "The subscription that you are attempting to confirm does not exist.");
                          break;

                      // The confirmation failed
                      default:
                          ModelState.AddModelError(String.Empty, "The confirmation of your newsletter subscription did not succeed.");
                          break;
                  }           

                  // If the subscription confirmation was not successful, displays a view informing the user
                  return View(model);
              }


      ```

3. Create a view for informing users about the result of their subscription confirmation requests.

When a new recipient clicks the link in the confirmation email received after subscribing to a newsletter with double opt-in enabled, the confirmation action on your MVC site attempts to confirm the subscription. If the link is valid (based on the expiration interval configured in the Xperience application in _Settings -> On‑line marketing -> Email marketing -> Double opt-in interval_), the system updates the recipient to the "Subscribed" status and the newsletter subscription becomes active.

The content of the subscription approval page depends on your implementation of the returned views.

## Validating email feed URLs

The **Email marketing** application in the Xperience administration interface automatically checks the accessibility of the URLs specified for each email feed. This includes the following:

- Unsubscription page URL (see [Setting up unsubscription](#setting-up-unsubscription))
- Approval page URL (see [Handling confirmations for double opt-in subscription](#handling-confirmations-for-double-opt-in-subscription))
- Clicked link and Opened email tracking URLs (see [Enabling marketing email tracking](https://docs.kentico.com/13/on-line-marketing-features/configuring-and-customizing-your-on-line-marketing-features/configuring-email-marketing/enabling-marketing-email-tracking.md))

If any of the given URLs are not accessible, the system displays a warning on the **Emails** tab of the email feed and the **Send** tab of individual emails.
