HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

CMP DAM in CMS

Integrate Optimizely Digital Asset Management (DAM) with Optimizely Content Management System (CMS) 12 for seamless access to digital assets.

The Optimizely Digital Asset Management (DAM) and Optimizely Content Management System (CMS) 12 integration lets editors select, manage, and render assets from DAM directly within the CMS user interface (UI).

The integration provides the following capabilities:

  • Select images, videos, and files from DAM using the library picker UI.
  • Retrieve and cache asset metadata (alt text, dimensions, descriptions).
  • Track asset usage in published content.
  • Render assets with proper metadata using built-in HTML helpers.
  • Query assets with Optimizely Graph for improved performance.
🚧

Important

Use the Graph integration for metadata retrieval in production environments. The CMP REST API has rate limiting that can result in 429 (too many requests) errors under heavy load. The Graph integration provides better performance and scalability and eliminates rate-limiting issues. See Metadata retrieval methods for details.

Prerequisites

Minimum version requirements

  • EPiServer.Cms.Core 12.22.1 or later
  • EPiServer.Cms.UI 12.32.0 or later
  • EPiServer.Cms.WelcomeIntegration.UI 2.2.0 or later

CMP requirements

  • Active Content Marketing Platform (CMP) account with application credentials (Client ID and Client Secret).
  • You can only configure one CMP instance per CMS instance.

Image processing requirements

For proper image format support, especially for modern formats like WebP and AVIF:

  • Use EPiServer.ImageLibrary.ImageSharp version 2.0.5 or later.
  • Obtain an ImageSharp license (required for version 2.x).
  • Upgrade to version 2.x for significantly better support for various image formats.
📘

Note

If images from DAM appear broken in the CMS content selector or preview, upgrading to ImageSharp 2.x typically resolves the issue.

Before troubleshooting any issues, verify you are running the latest versions of these packages.

Install the NuGet packages

Install the required NuGet packages:

# Required packages
dotnet add package EPiServer.CMS.WelcomeIntegration.UI
dotnet add package EPiServer.Cms.WelcomeIntegration.Core
dotnet add package Optimizely.Cmp.Client

# Recommended for production deployments
dotnet add package EPiServer.Cms.WelcomeIntegration.Graph
📘

Note

Install the Graph integration package in all production environments to avoid REST API rate limiting and improve performance.

Architecture overview

The integration consists of three main components:

  1. Library picker UI – Lets editors browse and select DAM assets.
  2. Metadata retrieval – Pulls asset metadata (alt text, dimensions, and so on) using the CMP REST API or Graph.
  3. Asset usage tracking – Reports to CMP where assets are used in published content.

Metadata retrieval methods

Use the Graph integration for all production deployments. While the integration supports both retrieval methods, the CMP REST API has performance limitations and rate limiting that make it unsuitable for high-traffic scenarios.

Option 1: CMP REST API (not recommended for production)

  • Direct API calls to CMP.
  • Subject to rate limiting that can return 429 (too many requests) status codes under heavy use.
  • Slower performance under heavy load.
  • Use only for development or low-traffic scenarios.

Option 2: Optimizely Graph (recommended)

  • Queries indexed asset data.
  • No rate-limiting issues.
  • Better performance and scalability.
  • Handles high-volume requests without throttling.
  • Requires additional configuration. See Enable Graph integration for details.

When metadata is retrieved

Asset metadata syncs on two occasions:

  • Content publishing – When publishing CMS content that references CMP assets.
  • Scheduled maintenance – Through the Optimizely DAM Maintenance scheduled job.

Configure the integration

  1. Enable the library picker UI – Add the following to your Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        // Enable CMP DAM library picker
        services.AddDAMUi(options => options.Enabled = true);
    
        // ... other service configuration
    }
  2. Configure CMP API integration – Create an application in CMP to obtain your Client ID and Client Secret, then configure authentication.

    🚧

    Important

    Ensure you are configuring credentials for the same CMP instance that editors will use. Mixing CMP instances causes metadata sync failures.

    • Option 1 – Configure in Startup.cs.
      services.AddOptimizelyCmpClient(options =>
      {
          options.ClientId = "YOUR_CLIENT_ID";
          options.ClientSecret = "YOUR_CLIENT_SECRET";
      });
    • Option 2 – Configure in appsettings.json (recommended for secrets management).
      {
        "Optimizely": {
          "Cmp": {
            "ClientId": "YOUR_CLIENT_ID",
            "ClientSecret": "YOUR_CLIENT_SECRET"
          }
        }
      }
      Then in Startup.cs:
      services.AddOptimizelyCmpClient();
  3. Enable Graph integration (recommended) – Configure Graph integration for all production deployments. The CMP REST API implements rate limiting that returns 429 (too many requests) status codes under heavy load, which becomes problematic in high-traffic environments or when multiple concurrent requests are made to CMP. Graph integration eliminates these issues, handles high-volume requests without rate limiting, and scales effectively for production workloads.

    This is additional configuration on top of the CMP API setup in step 2.

    First, ensure your Optimizely Graph gateway is configured. See Optimizely Graph documentation for details.

    • Option 1 – Configure in Startup.cs.

      services.Configure<CMPGraphOptions>(options =>
          options.SingleKey = "YOUR_CMP_GRAPH_SECRET");
      services.AddDAMGraphIntegration();
    • Option 2 – Configure in appsettings.json.

      {
        "Optimizely": {
          "CmpGraph": {
            "SingleKey": "YOUR_CMP_GRAPH_SECRET"
          }
        }
      }

      Then in Startup.cs:

      services.AddDAMGraphIntegration();
    📘

    Note

    Graph integration was introduced in version 1.4.0. Upgrade to this version or later to enable Graph-based metadata retrieval.

Advanced configuration (optional)

Customize DAM UI behavior in appsettings.json:

{
  "EPiServer": {
    "Cms": {
      "DAMUi": {
        "Enabled": true,
        "IconClass": "dijitNoIcon",
        "AvailableTypes": "episerver.core.imagedata",
        "StoreName": "episervercmsdamcontentcreation",
        "Endpoint": "https://cmp.optimizely.com",
        "Path": "/cloud/library-picker",
        "StartingFolders": {
          "Global": "/path/to/default/folder",
          "ByAssetType": {
            "image": "/images",
            "video": "/videos"
          }
        }
      }
    }
  }
}

The following parameters are available:

ParameterDescriptionDefault
EnabledEnables or disables DAM integrationtrue
IconClassCSS class for UI icondijitNoIcon
AvailableTypesRestricts integration to specific content typesepiserver.core.imagedata
EndpointCMP environment URLhttps://cmp.optimizely.com
StartingFoldersDefault folders in library pickerNone

Use assets in content

Supported property types

You can use CMP assets in the following property types:

  • ContentReference (single asset)
  • ContentReference list (multiple assets)
  • ContentArea
  • Url or UrlToImage (URL-based properties)
  • LinkItem (link properties)

When replacing an existing DAM asset, the library picker opens pre-selected on the current asset, regardless of the underlying property type. This deep linking works for ContentReference-based and URL-based properties.

The following example shows a content type that uses CMP assets:

public class ArticlePage : PageData
{
    [Display(Name = "Hero Image")]
    public virtual ContentReference HeroImage { get; set; }

    [Display(Name = "Gallery")]
    public virtual IList<ContentReference> ImageGallery { get; set; }

    [Display(Name = "Main Content")]
    public virtual ContentArea MainContent { get; set; }

    [Display(Name = "Background Image")]
    public virtual Url BackgroundImage { get; set; }
}

Asset types

CMP provides three asset categories:

  • Images – JPG, PNG, WebP, GIF, and so on.
  • Videos – MP4, MOV, and so on.
  • Raw files – PDFs, Word documents, Excel spreadsheets, PowerPoint presentations, ZIP archives, and so on.

Render assets

The integration provides built-in helpers to render assets with proper metadata. Use these helpers instead of manually constructing URLs.

The rendering helpers reuse cached metadata from the CMS database when available, reducing outbound calls to CMP Graph. This improves performance when rendering multiple assets on a single page.

Option 1: HTML helper (Razor)

The HTML helper provides multiple overloads for flexibility.

The following example shows basic usage with a ContentReference property:

@model PageViewModel<ArticlePage>
@using EPiServer.Cms.WelcomeIntegration.UI.Helpers

@await Html.RenderTagWithMetadata(m => m.CurrentPage.HeroImage)

To add HTML attributes to the rendered tag:

@await Html.RenderTagWithMetadata(
    m => m.CurrentPage.HeroImage,
    new Dictionary<string, string>
    {
        { "class", "hero-image" },
        { "loading", "eager" },
        { "fetchpriority", "high" }
    }
)

To use the asset GUID directly (for example, from custom properties):

@await Html.RenderTagWithMetadata(m => m.CurrentPage.AssetGuid)

Option 2: Tag helper (Razor)

The tag helper provides a declarative approach with support for custom attributes.

The following example shows basic usage:

@model PageViewModel<ArticlePage>
@addTagHelper *, EPiServer.Cms.WelcomeIntegration.UI

<dam-asset content-reference="@Model.CurrentPage.HeroImage" />

To add custom attributes using the attr- prefix:

<dam-asset
    content-reference="@Model.CurrentPage.HeroImage"
    attr-class="hero-image responsive"
    attr-loading="eager"
    attr-fetchpriority="high"
    attr-data-analytics="hero-banner" />

This renders as:

<img src="..."
     class="hero-image responsive"
     loading="eager"
     fetchpriority="high"
     data-analytics="hero-banner"
     alt="..."
     title="..." />

Option 3: URL helper extensions

For more control over rendering, use the URL helper extensions:

@using EPiServer.Cms.WelcomeIntegration.UI.Helpers

<!-- Get asset URL -->
<img src="@Url.DamAssetUrl(Model.CurrentPage.HeroImage)" alt="Hero" />

<!-- Get specific rendition URL -->
<img src="@Url.DamAssetUrl(Model.CurrentPage.HeroImage, "thumbnail")" alt="Thumbnail" />

Option 4: ContentArea with view components

When assets are added to a ContentArea, the integration uses view components to render them with full metadata:

@Html.PropertyFor(m => m.CurrentPage.MainContent)

The following view components handle rendering:

  • DAMImageAssetViewComponent – Renders image assets.
  • DAMVideoAssetViewComponent – Renders video assets.

No additional configuration is required; the view components are registered automatically.

Default rendering behavior

Images

Images render with the following default attributes:

  • loading="lazy" – Defers loading until the image is near the viewport.
  • decoding="async" – Lets the browser decode the image asynchronously.
  • fetchpriority="low" – Indicates the image has lower priority for bandwidth allocation.
  • width and height – Set from metadata to prevent layout shift (CLS optimization).
  • alt – Set from CMP metadata.
  • title – Set from CMP metadata.

The following example shows the rendered HTML output:

<img src="https://images2.cmp.optimizely.com/..."
     alt="A very cute little ghost"
     title="ghost.png"
     width="1130"
     height="1147"
     loading="lazy"
     decoding="async"
     fetchpriority="low" />

Override these defaults using the tag helper's attr- attributes or the HTML helper's dictionary parameter.

Videos

Videos render with an HTML5 <video> element:

<video controls>
    <source src="https://images2.cmp.optimizely.com/..." type="video/mp4" />
    Your browser does not support HTML5 video.
</video>

The integration sets the following video attributes:

  • Adds controls for playback controls.
  • Includes fallback text for browsers without HTML5 video support.
  • Sets the appropriate type attribute based on MIME type.

Documents and files (raw files)

All document and file types render as download links with target="_blank". This includes any asset with an application/* MIME type (PDF, Office documents, ZIP archives, and so on), text/* MIME type (CSV, plain text, and so on), or any other non-image, non-video MIME type.

The following example shows the rendered HTML output:

<a href="https://images2.cmp.optimizely.com/..."
   target="_blank"
   title="Annual Report 2025"
   alt="Annual Report 2025">Annual Report 2025</a>

Work with renditions

CMP assets can have multiple renditions, which are pre-processed versions optimized for different use cases (thumbnails, banners, mobile sizes, and so on).

Renditions

Renditions are alternative versions of an asset created in CMP with different dimensions, quality settings, or formats. Common examples:

  • Thumbnail – Small preview (for example, 150x150).
  • Banner – Wide format for hero images (for example, 1920x600).
  • Mobile – Optimized for mobile devices (for example, 640x480).

Access renditions

To retrieve a rendition URL using the URL helper:

<!-- Use specific rendition -->
<img src="@Url.DamAssetUrl(Model.CurrentPage.Image, "thumbnail")" alt="Thumbnail" />

<!-- Fallback: if "banner" does not exist, returns the default asset URL -->
<img src="@Url.DamAssetUrl(Model.CurrentPage.Image, "banner")" alt="Banner" />

Rendition matching follows these rules:

  • Case-insensitive, for example, "Thumbnail," "THUMBNAIL," and "thumbnail" all match.
  • If the requested rendition does not exist, the default asset URL is returned.
  • When a rendition is selected, the returned URL, width, and height reflect the rendition's values, not the original asset's.
  • Check application logs for warnings about missing renditions.

Access renditions programmatically

Use IDAMAssetIdentityResolver to query available renditions:

var identity = _resolver.Get(assetRef);
var renditions = identity?.Metadata?.DAMAssetInfo?.Renditions;

if (renditions != null)
{
    foreach (var rendition in renditions)
    {
        // rendition.Name - for example, "thumbnail"
        // rendition.Url - Direct URL to the rendition
        // rendition.Width - Rendition width in pixels
        // rendition.Height - Rendition height in pixels
    }
}

Troubleshoot renditions

If you request a rendition that does not exist, the following warning displays in application logs:

Rendition 'thumbnail' for asset with ContentReference '2_dam' is not found in DAM metadata, falling back to default asset URL.

The URL helper falls back to the default asset URL, so the page continues to work without errors, and there is no user-facing impact.

To resolve missing rendition warnings:

  1. Verify the rendition exists in CMP for the specific asset.
  2. Check the rendition name spelling (case-insensitive but must match exactly).
  3. Run the Optimizely DAM Maintenance scheduled job to refresh metadata.
  4. Rerun the job to update stale metadata if you recently added the rendition in CMP.

Access asset metadata programmatically

For custom rendering scenarios, use IDAMAssetIdentityResolver:

using EPiServer.Cms.WelcomeIntegration.Core;

public class CustomImageRenderer
{
    private readonly IDAMAssetIdentityResolver _resolver;

    public CustomImageRenderer(IDAMAssetIdentityResolver resolver)
    {
        _resolver = resolver;
    }

    public string RenderImage(ContentReference assetRef)
    {
        var identity = _resolver.Get(assetRef);
        if (identity?.Metadata?.DAMAssetInfo == null)
            return string.Empty;

        var info = identity.Metadata.DAMAssetInfo;

        return $@"<img
            src=""{info.Url}""
            alt=""{info.AltText}""
            width=""{info.Width}""
            height=""{info.Height}""
            title=""{info.Title}"" />";
    }
}

DAMAssetInfo structure

The DAMAssetInfo object contains comprehensive metadata:

{
  "Title": "ghost.png",
  "Url": "https://images2.cmp.optimizely.com/...",
  "Width": "1130",
  "Height": "1147",
  "Description": "Image description",
  "AltText": "A very cute little ghost",
  "MimeType": "image/png",
  "Renditions": [
    {
      "Name": "thumbnail",
      "Url": "https://images2.cmp.optimizely.com/...",
      "Width": "150",
      "Height": "150"
    },
    {
      "Name": "banner",
      "Url": "https://images2.cmp.optimizely.com/...",
      "Width": "1920",
      "Height": "600"
    }
  ],
  "FocalPoint": {
    "X": 500,
    "Y": 300
  }
}

The following properties are available on the DAMAssetInfo object:

PropertyTypeDescription
Titlestring?Asset title from CMP
UrlstringDirect URL to the asset (may be SEO-friendly)
Widthstring?Image width in pixels
Heightstring?Image height in pixels
Descriptionstring?Asset description from CMP
AltTextstring?Alternative text for accessibility
MimeTypestring?MIME type (for example, "image/png", "video/mp4")
RenditionsIEnumerable<DamAssetRendition>?Available renditions
FocalPointDamAssetFocalPoint?Smart crop focal point (X, Y coordinates)

Focal point

The FocalPoint property indicates the point of interest in an image, which is useful for smart cropping:

var identity = _resolver.Get(assetRef);
var focalPoint = identity?.Metadata?.DAMAssetInfo?.FocalPoint;

if (focalPoint != null)
{
    var x = focalPoint.X; // X coordinate (pixels from left)
    var y = focalPoint.Y; // Y coordinate (pixels from top)

    // Use focal point for custom cropping or positioning
}

Use the focal point for the following scenarios:

  • Responsive image cropping that preserves the main subject.
  • CSS object-position calculations.
  • Custom image processing pipelines.

Metadata limitations

CMS 12 supports the following metadata from CMP:

  • Title
  • Alt text
  • Description
  • Dimensions (Width, Height)
  • URL (including SEO-friendly URLs)
  • Renditions
  • Focal point

The following CMP metadata fields are not available in CMS 12:

  • Fields – Custom metadata fields defined in CMP.
  • Tags – Asset tagging and categorization.
  • Custom properties – Any CMP-specific custom properties.

Workarounds for extended metadata

If you need access to fields, tags, or other advanced CMP metadata, use one of the following approaches:

  • Query CMP Graph directly
    // Use Optimizely Graph to query CMP data directly
    var query = @"query {
        Asset(where: { _metadata: { key: { eq: ""YOUR_ASSET_ID"" } } }) {
            fields
            tags
        }
    }";
  • Retrieve with the CMP REST API – Use the CMP client to fetch full asset metadata and store relevant extended metadata in CMS content properties.
  • Duplicate in CMS – Store critical metadata fields directly on your CMS content types and maintain sync manually or with custom scheduled jobs.

Scheduled jobs

The integration provides two scheduled jobs that run automatically:

  • Optimizely DAM maintenance job
  • Optimizely DAM asset tracking job

Optimizely DAM maintenance job

  • Purpose – Syncs asset metadata from CMP to CMS.
  • Schedule – Configurable (default: daily).

The job performs the following actions:

  • Updates alt text, dimensions, and other metadata for all referenced assets.
  • Syncs SEO-friendly URLs if enabled in CMP.
  • Processes assets that failed during previous runs.

Troubleshoot the DAM maintenance job

  • Missing or outdated alt text – If assets render without alt text or with outdated metadata:
    • Verify content is published – Metadata only updates on publish, not on save.
    • Run the job manually – Go to Admin > Scheduled Jobs and run Optimizely DAM Maintenance.
    • Check application logs – Check application logs for errors during job execution.
    • Verify the rendering method – Ensure you are using @Html.RenderTagWithMetadata or <dam-asset> instead of manually constructing URLs with IUrlResolver.
  • SEO URLs not updating – If CMP has SEO-friendly URLs enabled, but CMS still uses hashed URLs:
    • Run the Optimizely DAM Maintenance job manually.
    • Verify the Graph integration is configured correctly (if using).
    • Check logs for sync errors.
    • Ensure the scheduled job completed successfully.
  • Partial job failures – If the job completes with partial success (for example, 999 of 1000 items processed):
    • Check application logs – Check application logs during the job execution timeframe.
    • Look for handled exceptions – Some errors are logged but do not fail the entire job.
    • Verify specific asset failures – Verify the following:
      • The asset still exists in CMP.
      • The asset is accessible with configured credentials.
      • The asset is from the correct CMP instance.

Optimizely DAM asset tracking job

  • Purpose – Reports asset usage to CMP Asset Lineage API.
  • Schedule – Configurable (default: daily).

The job performs the following actions:

  • Sends page URLs where assets are used to CMP.
  • Enables the CMP Usage tab to show where assets appear.
  • Helps prevent accidental deletion of in-use assets
    📘

    Note

    Only published content is tracked. Assets in unpublished or draft content do not appear in CMP usage reports.