---
title: Consent development for profiles
related:
  - https://docs.kentico.com/documentation/developers-and-admins/data-protection/consent-management.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/customer-data-platform.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).

> **Warning:** **Preview feature**
>
> The customer data platform feature is currently in preview mode. Expect the feature to be modified and expanded significantly in upcoming releases, including breaking changes.
>
> The initial releases of the feature are primarily intended to gather early feedback. The scope of the feature is [limited](https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/customer-data-platform.md#supported-features) and much of the Xperience by Kentico digital marketing functionality is not supported yet.
>
> All related API is [marked as experimental](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/general#experimental-attributes) and usage will result in warnings when compiling your project. The warnings are treated as errors for reporting purposes. To use the code, you need to [suppress](https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/experimental-overview#suppress-warnings) the warnings.
>
> What should you do with this feature?
>
> - **DO** try out this feature, for example, using the sample _Dancing Goat_ [project template](https://docs.kentico.com/documentation/developers-and-admins/installation.md) or other demo sites.
> - **DO** feel free to share your feedback with the Kentico [Product team](https://roadmap.kentico.com/).
> - **DON'T** use this feature in a production environment or invest heavily into development based on the current state.

This page describes how to set up consent functionality on Xperience websites when the [customer data platform](https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/customer-data-platform.md) feature is enabled.

In Xperience, consents are records used for informing visitors about the means of collecting and manipulating their personal data by the system, administrators, marketers and anyone else who has access to said data, including third parties to whom this data is forwarded.

You can use consents to comply with the requirements of [GDPR](https://docs.kentico.com/documentation/developers-and-admins/data-protection/gdpr-compliance.md) and other personal data regulations.

Whenever any personal data of a visitor is obtained, it is necessary to have a consent agreement from the visitor to legally process this data. This includes [tracking of profiles](https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/customer-data-platform.md) and their [activities](https://docs.kentico.com/documentation/business-users/digital-marketing/contact-activities.md). Every consent agreement of a visitor is directly connected to a corresponding contact (encapsulated by a profile). Deleting profiles from the system therefore also causes deletion of the related consent agreements.

To learn how to create consents in the Xperience administration, see [Consent management](https://docs.kentico.com/documentation/developers-and-admins/data-protection/consent-management.md).

## Set up tracking consent

When creating a consent for the [tracking of profiles](https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/customer-data-platform.md#profile-tracking) and [activities](https://docs.kentico.com/documentation/developers-and-admins/digital-marketing-setup/set-up-activities.md), you need to provide a way for visitors to adjust their allowed [cookie level](https://docs.kentico.com/documentation/developers-and-admins/data-protection/cookies.md). The system only performs the tracking for visitors whose cookie level is _Visitor_ or higher.

To ensure that visitors are not tracked until they give the tracking consent, set the **default cookie level** in the Xperience administration:

1. Open **Channel management** application.
2. Select your website channel.
3. Open the **Channel settings** tab.
4. In the **Cookies** section, choose a **Default cookie level**.
   - For an overview of individual cookie levels, see [Cookie levels](https://docs.kentico.com/documentation/developers-and-admins/data-protection/cookies.md#cookie-levels).
5. Select **Save**.

To allow visitors to give agreements with the tracking consent on your site, you need to develop corresponding components. For example, you can implement tracking consent functionality as a [view component](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components).

### Create the consent

Start by [defining the tracking consent](https://docs.kentico.com/documentation/developers-and-admins/data-protection/consent-management.md) in the Xperience administration interface (in the **Data protection** application on the **Consents** tab).

### Create the model

We recommend creating a view model class in your project to represent data related to the tracking consent. Include the following properties:

- A `string` property for storing the consent's short text
- A `bool` property to indicate whether an agreement with the consent was given
- A `string` property for passing the current page URL as the return URL

```csharp title="View model"
public class TrackingConsentViewModel
{
    public string ShortText { get; set; }

    public bool IsAgreed { get; set; }

    public string ReturnUrl { get; set; }
}
```

### Create the view component

Create a new [view component](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) class in your project:

- Initialize instances of services, which are necessary to manage the tracking consent:

  - `IInfoProvider<ConsentInfo>` – to load consent data.
  - `IProfileConsentAgreementService`– to manage consent agreements.
  - `ICurrentCookieLevelProvider`– to manage the current visitor's cookie level.
  - `IWebPageDataContextRetriever` and `IWebPageUrlRetriever` – to obtain the URL of the current page.
  - `IPreferredLanguageRetriever` – to get the currently selected content language.

  > **Tip:** Use [dependency injection](https://docs.kentico.com/documentation/developers-and-admins/development/website-development-basics/dependency-injection.md) to initialize service instances.

  ```csharp title="Services and using statements"
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Mvc;

  using CMS.DataEngine;
  using CMS.DataProtection;
  using CMS.Websites;

  using Kentico.Content.Web.Mvc;
  using Kentico.Content.Web.Mvc.Routing;
  using Kentico.CustomerDataPlatform.Web.Mvc;

  public class TrackingConsentViewComponent : ViewComponent
  {

      private readonly IProfileConsentAgreementService profileConsentAgreementService;
      private readonly IInfoProvider<ConsentInfo> consentInfoProvider;
      private readonly IWebPageDataContextRetriever webPageDataContextRetriever;
      private readonly IWebPageUrlRetriever urlRetriever;
      private readonly IPreferredLanguageRetriever preferredLanguageRetriever;

    public TrackingConsentViewComponent(IProfileConsentAgreementService profileConsentAgreementService,
                                        IInfoProvider<ConsentInfo> consentInfoProvider,
                                        IWebPageDataContextRetriever webPageDataContextRetriever,
                                        IWebPageUrlRetriever urlRetriever,
                                        IPreferredLanguageRetriever preferredLanguageRetriever)
      {
          this.profileConsentAgreementService = profileConsentAgreementService;
          this.consentInfoProvider = consentInfoProvider;
          this.webPageDataContextRetriever = webPageDataContextRetriever;
          this.urlRetriever = urlRetriever;
          this.preferredLanguageRetriever = preferredLanguageRetriever;
      }
  }
  ```
- Implement the component's `InvokeAsync` method to display information about the tracking consent:

  ```csharp title="Invoke the component"
  public async Task<IViewComponentResult> InvokeAsync()
  {
      // Gets the related tracking consent
      // Fill in the code name of the appropriate consent in Xperience
      ConsentInfo consent = await consentInfoProvider.GetAsync("SampleTrackingConsent");

      if (consent != null)
      {
          string consentName = consent.ConsentName;

          // Gets the code name of the currently selected language
          var preferredLanguage = preferredLanguageRetriever.Get();

          var consentModel = new TrackingConsentViewModel
          {
              // Adds the consent's short text to the model
              ShortText = (await consent.GetConsentTextAsync(preferredLanguage)).ShortText,

              // Checks whether the current profile has given an agreement for the tracking consent
              IsAgreed = await profileConsentAgreementService.IsAgreed(consentName, cancellationToken: HttpContext.RequestAborted),

              // Adds the current page URL to the model
              ReturnUrl = webPageDataContextRetriever.TryRetrieve(out var currentWebPageContext)
                  ? (await urlRetriever.Retrieve(currentWebPageContext.WebPage.WebPageItemID, preferredLanguage, cancellationToken: HttpContext.RequestAborted)).RelativePath
                  // Gets the URL from the request context for pages using custom routing
                  : (HttpContext.Request.PathBase + HttpContext.Request.Path).Value

          };

          // Displays a view with tracking consent information and actions
          return View("~/Components/ViewComponents/TrackingConsent/_TrackingConsent.cshtml", consentModel);
      }

      return Content(string.Empty);
  }
  ```

### Create the view

Add the view referenced in your view component's invoke method. Display the text of the tracking consent and inputs for giving or revoking agreements. Depending on your preferences and legal requirements, you can hide the view's output for visitors who have given the tracking consent or display alternative content (for example the option to [revoke the consent](#revoke-consents)).

```cshtml title="Tracking consent view"
@using Kentico.Content.Web.Mvc;

@model TrackingConsentViewModel

@* Displays content only if the current profile has not yet given a tracking consent agreement *@
@if (!Model.IsAgreed)
{
    var routeData = new Dictionary<string, string> { { "returnUrl", Model.ReturnUrl } };

    <div>
        @* Displays the consent text*@
        @Html.Raw(@Model.ShortText)
    </div>
    @* Displays an input for giving a consent agreement *@
    @* Replace "TrackingConsent" with the name of your controller that handles consent agreement requests *@
    <form asp-action="Agree"
          asp-controller="TrackingConsent" 
          asp-antiforgery="true"
          asp-all-route-data="routeData"
          method="post">
        <input type="submit" value="I agree">
    </form>
}
```

### Create the controller

Add a controller that handles requests for adding consent agreements:

- Initialize instances of services required to manage the tracking consent (some of the same services described for the [View component code](#create-the-view-component)).
- Define a POST action for creating consent agreements.
- Modify the visitor's cookie level according to the performed action – call the `SetCurrentCookieLevel` method of the `ICurrentCookieLevelProvider` service.
  - Set the _All_ cookie level when creating tracking consent agreements.
- Create consent agreements using the `TryAgree` method of the `IProfileConsentAgreementService` instance.

  ```csharp title="Tracking consent controller with an Agree POST action"
  using System.Threading;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Http;
  using Microsoft.AspNetCore.Mvc;

  using CMS.DataEngine;
  using CMS.DataProtection;
  using CMS.Helpers;

  using Kentico.CustomerDataPlatform.Web.Mvc;

  public class TrackingConsentController(
      ICurrentCookieLevelProvider currentCookieLevelProvider,
      IProfileConsentAgreementService profileConsentAgreementService,
      IInfoProvider<ConsentInfo> consentInfoProvider) : Controller
  {
      [HttpPost]
      [ValidateAntiForgeryToken]
      public async Task<ActionResult> Agree(string returnUrl, CancellationToken cancellationToken)
      {
          // Gets the related tracking consent
          // Fill in the code name of the appropriate consent in Xperience
          ConsentInfo consent = await consentInfoProvider.GetAsync("SampleTrackingConsent");
          
          if (consent != null)
          {
              string consentName = consent.ConsentName;

              // Sets the visitor's cookie level to 'All' (enables profile tracking)
              currentCookieLevelProvider.SetCurrentCookieLevel(Kentico.Web.Mvc.CookieLevel.All.Level);

              // Adds a tracking consent agreement for the current profile
              if (await profileConsentAgreementService.TryAgree(consentName, cancellationToken))
              {
                  return Redirect(returnUrl);
              }
          }

          return new StatusCodeResult(StatusCodes.Status400BadRequest);
      }
  }
  ```

### Invoke the component

Invoke the view component from a suitable location in your website's content. For example, use the main layout to ensure that the input to give the tracking consent is displayed on all pages.

```cshtml title="_Layout example"
...

<html>
<head >
    ...
</head>
<body>
    <div>
        @* Invokes 'TrackingConsentViewComponent' *@
        <vc:tracking-consent />

        ...
```

Visitors on your website now have an option to give agreements with your tracking consent. New visitors are not tracked as profiles until they positively confirm that they agree with the tracking.

## Create conditions based on consents

If you have any functionality that stores or processes personal data, you can create conditions to ensure that it only runs for profiles who have given consent.

1. Initialize an instance of the `IProfileConsentAgreementService` interface and make it available in your code.
2. Obtain the code name of the related tracking consent.
3. Evaluate whether the profile has given an agreement with the given consent by calling the `IsAgreed` method of the `IProfileConsentAgreementService` instance.

## Revoke consents

To ensure compliance of your website with the GDPR or other data protection regulations, you need to give visitors the option to view and revoke their previously given consent agreements. We recommend creating a privacy page, where visitors can view the consents for which they have given agreements and potentially revoke them.

> **Info:** "It shall be as easy to withdraw as to give consent."
>
> (Source: [GDPR Article 7](http://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32016R0679#d1e2001-1-1), Paragraph 3)

The following sections describe how to create a privacy page using MVC architecture. The privacy page lists accepted consents for visitors, and allows them to view the full consent details or revoke their agreements.

- Create a model representing the list of consents for which visitors have given agreements.
- Create a model representing consent details.
- Create a controller that provides actions for displaying and revoking consents.
- Implement a handler for the `DataProtectionEvents.RevokeConsentAgreement`[global event](https://docs.kentico.com/documentation/developers-and-admins/customization/handle-global-events.md) to clear related cookies and personal data.
- Create views for the consent list and details pages.

### Create the models

- Add a model class that represents the list of consents for which visitors have given agreements.

  ```csharp title="List of consents"
  using System.Collections.Generic;
  using System.Linq;

  using CMS.DataProtection;

  public class ConsentListingModel
  {
      public IEnumerable<Consent> Consents { get; set; } = Enumerable.Empty<Consent>();
  }
  ```

  > **Info:** **`Consent` and `ConsentInfo` objects**
  >
  > The Xperience API uses `ConsentInfo` objects to work with consents in general. However, when you load consents for which a profile has given an agreement, the `ConsentAgreementService.GetAgreedConsents` method returns a collection of `Consent` objects. Instances of the `Consent` class provide the `GetConsentText` method, which automatically retrieves either the current texts of the given consent or the texts of the archived consent version for which the agreement was given.
  >
  > When displaying accepted consents to visitors, always work with `Consent` objects to ensure that the correct consent text version is used (for scenarios where the consent text was changed after agreements had been given).
  >
  > For more information, see the _View archived consents_ section of the [Consent management](https://docs.kentico.com/documentation/developers-and-admins/data-protection/consent-management.md) page.

  > **Tip:** **Tip**: You can directly use an `IEnumerable<Consent>` collection as a model if you do not need to work with any other data in your controller or views. However, having a dedicated model class allows you to extend the model based on future requirements.
- Add a model class that represents the full details of a consent, including properties for the short text, full text, and any other information about the consent you may need.

  ```csharp title="Consent properties model"
  public class ConsentDetailsModel
  {
      public string ConsentShortText { get; set; }

      public string ConsentFullText { get; set; }

      public string ConsentDisplayName { get; set; }
  }
  ```

### Create the controller

Create a new controller class or edit an existing one:

- Initialize instances of the `IProfileConsentAgreementService` and `IInfoProvider<ConsentInfo>` services (and any other required services), and make them available in the controller's code:

  ```csharp title="Controller class and required services"
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading;
  using System.Threading.Tasks;
  using Microsoft.AspNetCore.Mvc;

  using CMS.DataEngine;
  using CMS.DataProtection;

  using Kentico.Content.Web.Mvc.Routing;
  using Kentico.CustomerDataPlatform.Web.Mvc;

  public class PrivacyPageController(
                      IProfileConsentAgreementService profileConsentAgreementService,
                      IInfoProvider<ConsentInfo> consentInfoProvider,
                      IPreferredLanguageRetriever preferredLanguageRetriever) : Controller
  {
      // ...
  }
  ```

- Implement a GET action which retrieves a list of all consents accepted by the visitor:

  1. Get the consents for which the profile has given an agreement by calling the `GetAgreedConsents` method of the `IProfileConsentAgreementService` instance.
  2. Create an instance of the consent list model class and pass it to the view.

  ```csharp title="GET list of consents"
  /// <summary>
  /// Loads and displays consents for which visitors have given agreements.
  /// </summary>
  public async Task<ActionResult> Index()
  {
      var consentListingModel = new ConsentListingModel();

      // Gets all consents for which the current profile has given an agreement
      consentListingModel.Consents = await profileConsentAgreementService.GetAgreedConsents(HttpContext.RequestAborted);

      return View(consentListingModel);
  }
  ```

- Implement a GET action which retrieves the full details of a specified consent:

  1. Get the consents for which the current profile has given an agreement using the consent agreement service, and select the required consent based on the action's parameter. This approach ensures that the correct consent text version is used in cases where the text was changed after the agreement had been given.
  2. Create an instance of the consent details model class, set its properties, and pass it to the view.

  ```csharp title="GET consent details"
  /// <summary>
  /// Display details of the specified consent.
  /// </summary>
  public async Task<ActionResult> ConsentDetails(int consentId)
  {
      // Gets a list of consents for which the current profile has given an agreement
      IEnumerable<Consent> consents = await profileConsentAgreementService.GetAgreedConsents(HttpContext.RequestAborted);

      // Gets the consent matching the identifier for which the details were requested
      // Using this approach to get objects of the 'Consent' class is necessary to ensure that the correct consent text
      // is displayed, either from the current consent text or the archived consent version for which the agreement was given
      Consent consent = consents.FirstOrDefault(c => c.Id == consentId);

      // Displays the privacy page (consent list) if the specified consent identifier is not valid
      if (consent == null)
      {
          return View("Index");
      }

      // Gets the consent text in the currently selected language
      ConsentText consentText = consent.GetConsentText(preferredLanguageRetriever.Get());

      // Sets the consent's details into the view model
      var model = new ConsentDetailsModel
      {
          ConsentDisplayName = consent.DisplayName,
          ConsentShortText = consentText.ShortText,
          ConsentFullText = consentText.FullText
      };

      return View(model);
  }
  ```

- Implement a POST action that revokes agreements for a specified consent:

  1. Retrieve the required consent (by calling the `IInfoProvider<ConsentInfo>().Get` method).
  2. Create a revocation for the consent agreement using the `TryRevoke` method of the `IProfileConsentAgreementService` instance.

  ```csharp title="POST action"
  /// <summary>
  /// Revokes the current profile's agreement with the specified consent.
  /// </summary>
  [HttpPost]
  [ValidateAntiForgeryToken]
  public async Task<ActionResult> Revoke(int consentId, CancellationToken cancellationToken)
  {
      // Gets the related consent object
      ConsentInfo consent = await consentInfoProvider.GetAsync(consentId);

      if (consent != null)
      {
          var consentName = consent.ConsentName;

          // Revokes the consent agreement
          await profileConsentAgreementService.TryRevoke(consentName, cancellationToken);
      }

      return RedirectToAction("Index");
  }
  ```

### Add a RevokeConsentAgreement handler

Websites and their related functionality often contain multiple locations where visitors can revoke consents. To ensure that revocations are handled correctly in all cases, we recommend assigning a custom handler to the system's `DataProtectionEvents.RevokeConsentAgreement` [global event](https://docs.kentico.com/documentation/developers-and-admins/customization/handle-global-events.md). The handler provides a central place where you can perform additional actions whenever a consent agreement is revoked.

The following are typical scenarios to handle in the `RevokeConsentAgreement`event:

- **Setting the cookie level** – when revoking the [tracking consent](#set-up-tracking-consent), you also need to lower the visitor's [cookie level](https://docs.kentico.com/documentation/developers-and-admins/data-protection/cookies.md) to ensure that profile tracking is disabled. Call the `SetCurrentCookieLevel` method of the `ICurrentCookieLevelProvider` service and set the default cookie level from the website channel's settings.
- **Clearing personal data** – when a visitor revokes a consent agreement, you may also need to delete or anonymize certain types of personal data stored by the system. See also: [Personal data erasure](https://docs.kentico.com/documentation/developers-and-admins/data-protection/personal-data-erasure.md)

```csharp title="Example"
using System;

using CMS;
using CMS.Core;
using CMS.DataEngine;
using CMS.DataProtection;
using CMS.Helpers;

// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomDataProtectionModule))]

internal class CustomDataProtectionModule : Module
{
    private ICurrentCookieLevelProvider currentCookieLevelProvider;

    // Module class constructor, the system registers the module under the name "CustomDataProtection"
    public CustomDataProtectionModule()
        : base("CustomDataProtection")
    {
    }

    // Contains initialization code that is executed when the application starts
    protected override void OnInit(ModuleInitParameters parameters)
    {
        // Resolves service dependencies from the IoC container
        currentCookieLevelProvider = parameters.Services.GetRequiredService<ICurrentCookieLevelProvider>();

        // Assigns a handler to the RevokeConsentAgreement event
        DataProtectionEvents.RevokeConsentAgreement.Execute += RevokeConsentHandler;
    }

    private void RevokeConsentHandler(object? sender, RevokeConsentAgreementEventArgs e)
    {
        // For the tracking consent, lowers the cookie level to the website channel's default in order to disable tracking
        // Fill in the code name of the appropriate tracking consent in Xperience
        if (e.Consent.ConsentName.Equals("SampleTrackingConsent", StringComparison.OrdinalIgnoreCase))
        {
            currentCookieLevelProvider.SetCurrentCookieLevel(currentCookieLevelProvider.GetDefaultCookieLevel());
        } 
    }
}
```

### Create the views

Add appropriate views to your project to display the consent list (privacy page), the consent details and revocation actions.

> **Tip:** Consent text values may contain HTML tags added via the editor in the Xperience administration (e.g., for formatting). To ensure that the text is displayed correctly, disable the HTML encoding for the values using the `Html.Raw` method.
>
> ```cshtml title="Example"
> <h3>Short text</h3>
> <p>@Html.Raw(Model.ConsentShortText)</p>
> <h3>Full text</h3>
> <p>@Html.Raw(Model.ConsentFullText)</p>
> ```

Visitors on your site are now able to view a list of all consents for which they have given an agreement. They can also access the full details of the given consents and revoke their agreements if required.
