Digizuite.Optimizely 4.4.0

dotnet add package Digizuite.Optimizely --version 4.4.0
                    
NuGet\Install-Package Digizuite.Optimizely -Version 4.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Digizuite.Optimizely" Version="4.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Digizuite.Optimizely" Version="4.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Digizuite.Optimizely" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Digizuite.Optimizely --version 4.4.0
                    
#r "nuget: Digizuite.Optimizely, 4.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Digizuite.Optimizely@4.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Digizuite.Optimizely&version=4.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Digizuite.Optimizely&version=4.4.0
                    
Install as a Cake Tool

Digizuite DAM for Optimizely

Requirements

.NET Optimizely CMS EF Core
6.0 12.16.0 or later (CMS.UI 12.16.0+) 7 - 9
8.0 12.24.0 or later (CMS.UI 12.24.0+, CMS.Core 12.19.0+) 8 - 9
10.0 12.24.0 or later (CMS.UI 12.24.0+, CMS.Core 12.19.0+) 10

Optimizely CMS 13 is not supported.

Version 4.4.0 adds .NET 8 and .NET 10 support; .NET 6 is still supported, so existing sites can update without retargeting. Note that the Optimizely CMS 12 packages are themselves built for net6.0 on every version - it is your site's target framework that moves, never the CMS packages.

On .NET 6 the floors are the same as in 4.3.1, so 4.4.0 is a drop-in update. On .NET 8 and .NET 10 they follow the EPiServer.CMS 12.24.0 metapackage. Security advisories in the CMS dependency closure (for example in older CMS 12 releases) are resolved by upgrading Optimizely CMS in your site.

SQL Server compatibility level

On .NET 8 and .NET 10 the connector runs on EF Core 8 / 10, which translate Contains() over a list into OPENJSON. That requires the database to be at compatibility level 130 (SQL Server 2016) or higher. A database restored or attached from an older SQL Server keeps its original compatibility level even on a modern engine, so this is easy to hit unintentionally - the symptom is SqlException: Incorrect syntax near '$' (error 102) on the first asset load.

The connector therefore defaults to compatibility level 120, which uses the older IN (...) translation and works against any database. No action is needed when upgrading. The setting has no effect on .NET 6, where EF Core 7 always uses IN (...).

If your database is at compatibility level 130 or higher and you want the optimized translation:

{
  "DigizuiteDamSettings": {
    "SqlServerCompatibilityLevel": 160
  }
}

To check and raise your database instead:

SELECT name, compatibility_level FROM sys.databases WHERE name = DB_NAME();
ALTER DATABASE [YourDb] SET COMPATIBILITY_LEVEL = 130;

Managed Integration

Managed Integration lets the KeyShot DAM push asset events (create, update, delete) to your site. It is new in 4.4.0 and is separate from the existing Digizuite integration (DigizuiteWebhookElement) - the two use different DAM APIs and can be enabled independently.

Add a ManagedIntegration section to digizuitesettings.json:

{
  "DigizuiteDamSettings": {
    "ManagedIntegration": {
      "Enabled": true,
      "Name": "Optimizely-prod",
      "SiteUrl": "https://www.example.com",
      "Secret": "<provided by KeyShot>"
    }
  }
}
Setting Default Description
Enabled true Whether the DAM should send events. false still registers the integration, but registers it disabled, so it is a real off switch. Remove the whole section to opt out entirely. Nothing is registered until SiteUrl and Secret are also set.
Name Optimizely The integration's name in the DAM, and its upsert identity. See the warning below.
SiteUrl - This site's publicly reachable base url. The receiving path is appended to it.
Secret - The shared secret, sent as the x-dgz-managed-integration header. Issued by KeyShot - see below.
CloudflareContentCacheEnabled true Whether the site is served through the Cloudflare content cache. true registers the integration against the Cloudflare worker, which forwards to the site. false registers the site's own url, so the DAM sends events directly. See below.
CdnHostNames empty Extra FQDNs (no scheme) for the CDN purge, when assets are served from more than one domain. Ignored when CloudflareContentCacheEnabled is false.
MaxBatchSize 100 Assets per notification. The DAM's own ceiling is 200.
RequestTimeout 60 Seconds the DAM waits for a response.
LanguageId DAM default DAM language id for metadata in the payload.
RegisterOnStartup true Whether to create/update the integration in the DAM at startup.
AcknowledgeAssets true Whether to report an integration state per synced asset, which marks it tracked.

No change to Startup.cs is needed - AddDigizuiteDAM() wires up both halves. When the section is absent, nothing is registered with the DAM and no middleware is added.

How the chain works

With CloudflareContentCacheEnabled set to false there is no worker: the integration is registered directly against {SiteUrl}/keyshot/managedintegration/invoke, and the DAM sends events straight to your site. The rest of this section describes the default, true.

The integration is not registered against your site directly. It is registered against the Cloudflare worker at {DAM api url}/managed-integration, which purges the CDN cache and then forwards the notification to {SiteUrl}/keyshot/managedintegration/invoke.

Two consequences worth knowing:

  • The worker answers the DAM itself and never reads your site's response. A failed forward is logged in the worker and lost - it does not appear in the DAM's failed-notification list.
  • This requires the CDN Accelerated API to be enabled on your DAM.

Two things that catch people out

Name must be unique per environment. Registration upserts by name: the DAM is asked for its integrations, matched on this exact string, and the match is updated in place. Two environments pointing at the same DAM with the same name will overwrite each other's endpoint. Leaving Name unset logs a warning at startup for exactly this reason.

Secret is not a value you choose. The Cloudflare worker validates the header against its own configured secret and answers 401 if it differs, so it has to be the secret issued by the KeyShot service team. The worker forwards the header unchanged, which is what your site checks. (With CloudflareContentCacheEnabled set to false there is no worker, so the value only has to match between the DAM integration and your site.)

Setting the secret in production

AddDigizuiteDAM reads digizuitesettings.json through its own configuration builder, which has no environment-variable provider - DigizuiteDamSettings__ManagedIntegration__Secret binds to nothing. Supply it through the setup lambda instead, which is applied last and so wins over the JSON file:

services.AddDigizuiteDAM(cfg => cfg.ManagedIntegration = new ManagedIntegrationElement
{
    Name = "Optimizely-prod",
    SiteUrl = "https://www.example.com",
    Secret = Configuration["KeyShot:ManagedIntegrationSecret"]   // env var, Key Vault, ...
});

What happens when an event arrives

For each asset the DAM reports as created or updated:

  1. The stored blobs are compared against the notification, so only what went stale is dropped and re-fetched on next use:
    • Each rendition (transcode) is judged on the timestamp query parameter of its url, which moves when the DAM regenerates it - for example when a video's thumbnail is replaced. A rendition whose timestamp is unchanged is left alone. A missing timestamp, or a rendition that no longer exists, counts as changed.
    • The source file is judged on its MD5 (hashMd5). If it differs, the source blob is wiped, along with the asset's crops and other derived items. If it matches they are left alone — this is the whole point of the comparison, since re-fetching a large file no-one changed is expensive.
  2. The stored record is updated with the asset from the notification. No call back to the DAM is needed: the payload already carries the full asset.
  3. The memory cache for the asset is cleared, in every language, and the other servers in the cluster are told to do the same.

Assets reported as deleted are removed completely — record, blobs and cache.

Two things worth knowing:

  • Only the language the integration is registered with is refreshed in place. The payload carries one language, so the asset's records in any other language are dropped rather than left holding metadata of unknown age; they are re-read from the DAM on next access.
  • Access rights are not part of the payload, so the stored security entries are kept as they are. A permission change in the DAM therefore does not reach the site through this route until something else refreshes the asset. If that matters for your setup, say so — it needs a separate lookup per asset, which is why it is not done by default.

An asset the site has never loaded is left alone: there is nothing cached to update. The one exception is a new crop (an asset derived from another): it is stored under its parent, together with its access list, so it shows up in the parent's list of crops.

Verifying it

Set Digizuite.Optimizely to Information in your logging configuration. At startup you should see one line naming the integration, the worker endpoint and the forward url. When an asset changes in the DAM, you should see ManagedIntegration: received batch ... once per event.

If the startup line never appears, the section is missing or SiteUrl/Secret are blank. If it appears but no batches arrive, check that SiteUrl is reachable from the internet and that the secret matches - a mismatch is answered with 401 by the worker and leaves no trace on your side.

Each batch also logs what it did, which is the quickest way to see the hash comparison working:

ManagedIntegration: applied batch updated=1 cropsAdded=0 blobsWiped=0 alreadyCurrent=0 notCached=0 cleared=0 deleted=0 failed=0 assetIds=[1234].
  • blobsWiped=0 on a metadata-only edit, and 1 after replacing the source file or a rendition such as a video thumbnail. If it is 1 every time, your DAM is not populating hashMd5 and the connector is falling back to always wiping - correct, but slower than it needs to be.
  • alreadyCurrent counts assets that were already up to date, for example when the same change is sent again. They are skipped.
  • cleared counts assets whose payload could not be used, which fall back to being dropped rather than updated. Everything still works, just with a re-fetch per asset. If it is every asset, the integration is registered without the connector's metadata fields - the connector fills those in at startup, so this normally means registration did not run.

Get Started

DAM for Optimizely enables the full potential of the Digizuite DAM and provides access to all digital assets at any time from one single source of truth. Read more about all the benefits here: http://www.damforoptimizely.com To get started, you should add the Digizuite DAM services in your startup class. The simplest option to see it work right away is to try out the integration with a shared, readonly, default DAM. You can use that by just adding this service registration in your startup:

services.AddDigizuiteDAM();

With this done, you can already now try to go to edit-mode, and look in the Digizuite widget in the Asset panel to navigate the folders and assets on the DAM. You are also able to drag and drop them into your content and use them in your test or developer environment.

Usually you will want to connect to your own DAM - and set many other settings. You can see an example of how is should be done here: https://keyshot.atlassian.net/wiki/spaces/DD/pages/5628297794/DFO+4.4.0+-+3+Setting+up+Optimizely

Key Features

  • Digizuite Content Provider. Realtime mapping of Digizuite assets and folders into Optimizely
  • Digizuite Asset Browser. Powerful UI extension that allows you to easily find the assets you are looking for.
  • Multi-lingual assets. Assets from Digizuite can contain metadata in multiple languages - as opposed to Optimizelys default behavior.
  • Pseudo streaming of video. With the integration your Optimizely installation can proxy videos streaming live from the DAM.
  • Automatic encoding, conversion and optimization of all assets from Digizuite.
  • Full CRUD capabilities of Assets in Digizuite. You can both Create new assets (upload), Read and display assets, Update metadata on assets, and Delete assets from the Episerver channel in the DAM - all from within Optimizelys Edit mode.
  • Easy editor cropping to predefined formats. Editors can now from the right-click menu create crops of any asset from a predefined list of standardized crops that goes across your organization.
  • Developer managed media format selection. When developers render your assets they can decide between all the DAM-configured mediaformats and output the format that is needed, where it's needed.
  • Validation attributes for developers. Developers can control which assets editors can use in different locations, simply by attaching an attribute in the content model And much, much more. Learn more here: https://www.damforoptimizely.com/

Configuration

Most of the configuration of the integration to Digizuite can be done the configuration options in the Startup class. Once you have your own Digizuite DAM instance configured to work with DAM For Optimizely you can update the connection settings to ensure you connect to the proper DAM. At that time it would also be a good idea to customize the mapping of access control roles between Optimizely and Digizuite. There are also numerous other parameters you can set to your preferences. Read more here: https://keyshot.atlassian.net/wiki/spaces/DD/pages/5628297794/DFO+4.4.0+-+3+Setting+up+Optimizely

Developer customizations

Since DAM for Optimizely seamlessly integrates Digizuite assets directly into Optimizely as content, you can of course work with them as you would with any Optimizely based content media - including customizing their renderings, modifying their models and so on. But we have included a number of additional cool technologies you can implement as a developer. Learn more about typical customizations here: https://keyshot.atlassian.net/wiki/spaces/DD/pages/5628297981/DFO+4.4.0+-+Developers+Guide

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.4.0 73 10/7/2026
4.3.1 929 7/6/2026
4.3.0 152 7/3/2026
4.3.0-beta-1 116 7/2/2026
4.2.7 481 2/11/2026
4.2.6 167 2/10/2026
4.2.5 535 1/15/2026
4.2.4 433 12/15/2025
4.2.3 1,137 11/13/2025
4.2.2 380 10/15/2025
4.2.1 600 9/5/2025
4.2.0 461 8/12/2025
4.1.5 987 9/12/2025
4.1.4 370 8/27/2025
4.1.3 701 7/21/2025
4.1.2 846 6/24/2025
4.1.2-beta.2 212 6/19/2025
4.1.2-beta.1 239 6/18/2025
4.1.1 406 5/7/2025
4.0.9 297 9/11/2025
Loading failed

4.4.0 - Adds support for .NET 8 and .NET 10 alongside .NET 6.

For existing customers on .NET 6 this is a drop-in update unless your code calls connector
internals: configuration and the documented extension points are unchanged or only extended,
but some unused public helpers and models were removed and DigizuiteServiceWrapper has a new
constructor (see Breaking changes in the documentation). The Optimizely CMS, TinyMCE and EF Core
floors stay within what 4.3.1 already allowed (EPiServer.CMS.UI 12.16). The only dependency
change is Razor.Templating.Core 1.6.0 -> 2.x.

If you retarget your site to net8.0 or net10.0, the floors follow the EPiServer.CMS 12.24.0
metapackage: EPiServer.CMS.UI 12.24.0, EPiServer.CMS.Core 12.19.0 and TinyMCE 4.5.0, plus EF Core
8 or 10 respectively. Optimizely CMS 13 is not supported.

New setting DigizuiteDamSettings:SqlServerCompatibilityLevel (default 120). EF Core 8 and later
translate Contains() over a list into OPENJSON, which requires the database to be at compatibility
level 130 or higher; the default of 120 keeps the previous IN (...) translation and works against
any database. It has no effect on .NET 6. This also fixes the failure for customers who had
already moved to EF Core 8 or 9 on 4.3.1 and hit "Incorrect syntax near '$'".

Response headers are now set idempotently instead of throwing ArgumentException when the header
was already set upstream.

New feature: Managed Integration. The KeyShot DAM can now push asset events to the site through
the Cloudflare content cache, which purges the CDN and forwards to
{SiteUrl}/keyshot/managedintegration/invoke. Configured under
DigizuiteDamSettings:ManagedIntegration; requires the CDN Accelerated API on the DAM and a
secret issued by KeyShot. The integration is created or updated in the DAM at startup, matched
on its configured Name. No change to Startup.cs is needed, and nothing happens unless the
section is present. This is opt-in and does not affect the existing Digizuite integration
(DigizuiteWebhookElement), which continues to work unchanged.

On an asset event the connector compares the source file's MD5 against its stored copy and wipes
the asset's blobs, crops and derived items only when the file actually changed; the stored record
is then updated from the notification itself, with no call back to the DAM, and the memory cache
is cleared across the cluster. Deleted assets are removed entirely. Only the language the
integration is registered with is refreshed in place - other languages are dropped and re-read on
next access - and access rights, which are not part of the payload, are left as they are.
See the README for setup and verification.