---
title: Personalize content with a headless approach
slug: cms-12/api/personalizing-content
docTags: 
createdAt: 2026-08-07T16:45:43.000Z
---

:::hint{type="info"}
**Note**
This section applies only if you are still using Content Delivery Api v2.x.x. For users using Content Delivery API v3.x.x, see [Breaking change in v3](docId\:gffDqSzg10Kw8ISyDj6fx).
:::

How do you add content personalization to a headless solution where the tracking does not hit a controller in the presentation layer? You can use a headless approach for content recommendations through the Optimizely Content Delivery API. You must implement compatible tracking and calls to the recommendation API to do this.

In a headless scenario, you can also customize the `DefaultContentModelMapper` and replace it with your own. See [Customize data returned to clients](docId\:G0-aJ6fe4B2u8u7UHsx1L) and [Serialization](docId\:sdITUMRxuxBO9KF9stemS).

Example: Customizing `DefaultContentModelMapper`

You can manipulate with personalization by customizing the `DefaultContentModelMapper`.

1. Create a class where your `CustomContentModelMapper` inherits from `DefaultContentModelMapper`. Register it with `ServiceConfiguration` and in `DependencyResolverInitialization` (this is an example from an Alloy site).

```csharp
public void ConfigureContainer(ServiceConfigurationContext context) {
  //Implementations for custom interfaces can be registered here.

  context.ConfigurationComplete += (o, e) => {
    //Register custom implementations that should be used in favour of the default implementations
    context.Services.AddTransient<IContentRenderer, ErrorHandlingContentRenderer>()
      .AddTransient<ContentAreaRenderer, AlloyContentAreaRenderer>()
      .AddTransient<IContentModelMapper, CustomContentModelMapper>();
  };
}
```

2. With `CustomContentModelMapper`:

```csharp
[ServiceConfiguration(typeof (IContentModelMapper), Lifecycle = ServiceInstanceScope.Singleton)]
public class CustomContentModelMapper: DefaultContentModelMapper {
  public CustomContentModelMapper(IContentTypeRepository contentTypeRepository, ReflectionService reflectionService, IContentModelReferenceConverter contentModelService, IUrlResolver urlResolver, IEnumerable<IPropertyModelConverter> propertyModelConverters): base(contentTypeRepository, reflectionService, contentModelService, urlResolver, propertyModelConverters) {}

  public override ContentApiModel TransformContent(IContent content, bool excludePersonalizedContent = false, string expand = "") {
    var personalization = System.Web.HttpContext.Current.Request.Params["personalization"];
    return base.TransformContent(content, string.IsNullOrWhiteSpace(personalization) ? excludePersonalizedContent : Boolean.Parse(personalization), expand);
  }
}
```

This way, you can take advantage of personalization parameters in your request.

:::hint{type="info"}
**Note**
The `DefaultContentModelMapper` is currently marked as internal, so there is a risk of unadvertised breaking changes when customizing this.
:::
