---
title: Upgrade Content Delivery API from version 3 to version 13
slug: cms-13/docs/upgrade-content-delivery-api-to-13
createdAt: 2026-10-27T18:55:48.000Z
---

Content Delivery API is the REST API that serves CMS content to headless front ends and other client applications. Upgrade to version 13 when you move your solution from CMS 12 to CMS 13. The upgrade keeps those applications delivering content through a supported API.

Version 13 is the supported path for solutions that run Content Delivery API 3.x. Upgrading keeps your existing integration, so you do not need to rebuild it on another API.

Content Delivery API 13 runs on Optimizely CMS 13 and .NET 10, and serializes content with System.Text.Json instead of Newtonsoft.Json. It also removes Content Definitions API and Content Management API. This article covers the package updates, the serializer changes, and the endpoint changes that your client applications require.

The product version moves from 3 to 13 to match the CMS release it supports. The endpoint version is numbered independently of the product version, so Content Delivery API 13 uses the `/api/episerver/v4.0` base endpoint.

:::hint{type="warning"}
**Important**
The base endpoint moves from `/api/episerver/v3.0` to `/api/episerver/v4.0`. Every client application breaks until you update its API URLs. Plan a coordinated release of the solution and its client applications.
:::

For details about the CMS platform changes behind this upgrade, see [Upgrade to CMS 13 from CMS 12](docId:1Y3KoWaxY0cxXGcXMbXK8).

## Prerequisites

Before you start the upgrade, verify the following requirements:

- Your solution runs Optimizely CMS 13.0.2 or a later 13.x release.
- Your project targets .NET 10.
- Your solution runs Commerce Connect 15.0.0 or a later 15.x release, when the solution uses Commerce.
- Your solution runs Optimizely Forms 6.0.1 or a later 6.x release, when the solution uses Forms.
- You have access to update both the server-side project files and the client applications that call the API.
- You have a staging environment for testing the upgrade before production.
- You have an inventory of custom `IPropertyModel`, `ContentApiModelFilter`, and `IJsonConverter` implementations in the solution.
- You have an inventory of the calls your solution makes to Content Management API and Content Definitions API. Version 13 adds no replacement for either, so those calls need a separate integration. See [Content Definitions API and Content Management API](docId\:E87g81KtntgK-65scIk5D).

## Upgrade sequence

Complete the upgrade in the following order, because each step depends on the one before it:

1. Upgrade CMS to version 13 and retarget the project to .NET 10.
2. Update the Content Delivery API package references.
3. Replace the Newtonsoft.Json serializer configuration with System.Text.Json.
4. Remove the references to Content Definitions API and Content Management API.
5. Update the client applications to the new base endpoint and response model.
6. Build the solution and resolve the remaining errors.
7. Test the API responses against the client applications in a staging environment.

## Server-side changes

The changes in this section apply to the C# solution that hosts the API.

### Update NuGet packages

Update the package references in your project file to version 13.0.0:

```xml
<PackageReference Include="EPiServer.ContentDeliveryApi.Core" Version="13.0.0" />
<PackageReference Include="EPiServer.ContentDeliveryApi.Cms" Version="13.0.0" />
```

Solutions that use Commerce Connect also update the Commerce package:

```xml
<PackageReference Include="EPiServer.ContentDeliveryApi.Commerce" Version="13.0.0" />
```

### Remove Newtonsoft.Json dependencies

Content Delivery API 13.0 uses System.Text.Json. Remove the Newtonsoft.Json packages that served the Content API alone:

```xml
<!-- Remove when only the Content API required this package -->
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="..." />
```

:::hint{type="info"}
**Note**
Keep Newtonsoft.Json installed when other parts of your solution depend on it. The Content Delivery API packages no longer reference it.
:::

For the framework-wide Newtonsoft.Json removals in CMS 13, see [Framework and platform breaking changes](docId:1hM6Gyd6-GQ3nQ_ZtyRBA).

### Update JSON attribute usage

Update the model classes that carry Newtonsoft.Json attributes and feed the Content API. The following table maps each attribute to its System.Text.Json equivalent:

| Newtonsoft.Json                                                | System.Text.Json                                                |
| -------------------------------------------------------------- | --------------------------------------------------------------- |
| `using Newtonsoft.Json;`                                       | `using System.Text.Json.Serialization;`                         |
| `[JsonProperty("name")]`                                       | `[JsonPropertyName("name")]`                                    |
| `[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]` | `[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` |
| `[JsonIgnore]`                                                 | `[JsonIgnore]` (namespace change only)                          |
| `[JsonConverter(typeof(X))]`                                   | `[JsonConverter(typeof(X))]` (namespace change only)            |
| `[JsonExtensionData]`                                          | `[JsonExtensionData]` (namespace change only)                   |

### Update custom JSON converters

Custom JSON converters registered through `IJsonConverter` now extend `System.Text.Json.Serialization.JsonConverter<T>` rather than `Newtonsoft.Json.JsonConverter`. Version 3.x used the following pattern:

```csharp
public class MyConverter : Newtonsoft.Json.JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { ... }
    public override object ReadJson(JsonReader reader, Type type, object existing, JsonSerializer serializer) { ... }
    public override bool CanConvert(Type objectType) => objectType == typeof(MyModel);
}
```

Version 13.0 uses the following pattern:

```csharp
public class MyConverter : System.Text.Json.Serialization.JsonConverter<MyModel>
{
    public override void Write(Utf8JsonWriter writer, MyModel value, JsonSerializerOptions options) { ... }
    public override MyModel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { ... }
}
```

### Update serializer configuration

The serializer configuration callback receives `JsonSerializerOptions` from System.Text.Json rather than `JsonSerializerSettings` from Newtonsoft.Json. Version 3.x used the following configuration:

```csharp
services.ConfigureContentDeliveryApiSerializer(settings =>
{
    settings.NullValueHandling = NullValueHandling.Ignore;
    settings.ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new CamelCaseNamingStrategy()
    };
});
```

Version 13.0 uses the following configuration:

```csharp
services.ConfigureContentDeliveryApiSerializer(options =>
{
    options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
    options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    options.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
});
```

The following table maps each Newtonsoft.Json setting to its System.Text.Json equivalent:

| Newtonsoft.Json                | System.Text.Json                                               |
| ------------------------------ | -------------------------------------------------------------- |
| `NullValueHandling.Ignore`     | `DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull` |
| `CamelCaseNamingStrategy`      | `PropertyNamingPolicy = JsonNamingPolicy.CamelCase`            |
| `StringEnumConverter`          | `JsonStringEnumConverter(JsonNamingPolicy.CamelCase)`          |
| `MaxDepth = 32`                | `MaxDepth = 32`                                                |
| `MissingMemberHandling.Ignore` | Default behavior, so no action follows                         |
| `TypeNameHandling.None`        | Default behavior, so no action follows                         |

Solutions that use Commerce Connect apply the same changes to `ConfigureCommerceApiSerializer`.

### Set camelCase keys on custom property models

System.Text.Json does not apply `DictionaryKeyPolicy` to `[JsonExtensionData]` dictionary keys. This affects custom `IPropertyModel` implementations and custom `ContentApiModelFilter` implementations that add keys to `ContentApiModel.Properties`.

In version 3.x, the Newtonsoft.Json `CamelCasePropertyNamesContractResolver` converted every dictionary key to camelCase. `[JsonExtensionData]` keys received the same treatment. Version 3.x accepted a PascalCase key:

```csharp
contentApiModel.Properties.Add("PreviewUrl", value); // Serialized as "previewUrl"
```

In version 13.0, set the key in camelCase yourself:

```csharp
contentApiModel.Properties.Add("previewUrl", value); // Serialized as written
```

A custom property key sometimes collides with a built-in `ContentApiModel` property name after camelCase conversion. A content property named `Url` becomes `url`, which matches the built-in `url` property. Content Delivery API skips the colliding property to avoid duplicate JSON keys.

### Replace `ConfigureForExternalTemplates`

Version 13.0 deprecates `ConfigureForExternalTemplates()`. Register headless applications in CMS 13 as `RemoteWebsite` instead. A `RemoteWebsite` is an application type for an external front end. It holds the front-end host and edit URLs, and serves no templates from CMS.

Version 3.x used the following registration:

```csharp
services.AddContentApiCore();
services.ConfigureForExternalTemplates();
```

In version 13.0, configure the application as a `RemoteWebsite` in the CMS 13 admin interface, then remove the `ConfigureForExternalTemplates()` call. To disable template validation for content URLs, set the option directly:

```csharp
services.AddContentApiCore(options =>
{
    options.ValidateTemplateForContentUrl = false;
});
```

### Update the renamed site types

CMS 13 replaces the `SiteDefinition` model with the `Application` model, and Content Delivery API follows that change. Version 13.0 handles the renamed types in two ways. It removes some outright and marks others obsolete. The following table lists each type and the result at compile time:

| Version 3.x                                 | Version 13.0                                       | Compile result                                   |
| ------------------------------------------- | -------------------------------------------------- | ------------------------------------------------ |
| `SiteDefinitionModel`                       | `ApplicationModel`                                 | Error, because version 13.0 removes the type     |
| `ContentApiTrackingContext.ReferencedSites` | `ContentApiTrackingContext.ReferencedApplications` | Error, because version 13.0 removes the property |
| `SiteDefinitionLanguageModel`               | `ApplicationLanguageModel`                         | Warning, because the type stays as obsolete      |
| `DependencyTypes.Site`                      | `DependencyTypes.Application`                      | Warning, because the constant stays as obsolete  |
| `ReferencedSiteMetadata`                    | `ReferencedApplicationMetadata`                    | Warning, because the type stays as obsolete      |

Update every entry in the table, including the obsolete ones. A later release removes the obsolete types.

The `ReferencedApplicationMetadata` constructor also takes a different first argument. `ReferencedSiteMetadata` took a globally unique identifier (GUID), and `ReferencedApplicationMetadata` takes the application name as a string.

For the wider CMS 13 changes behind the `Application` model, see [Sites-to-applications and routing breaking changes](docId\:wcM3YGEeumSTzPx9raB32).

### Remove Search & Navigation package references

Version 13.0 drops the Content Search API packages. Remove the following package references:

```xml
<!-- Remove -->
<PackageReference Include="EPiServer.ContentDeliveryApi.Search" Version="..." />
<PackageReference Include="EPiServer.ContentDeliveryApi.Search.Commerce" Version="..." />
```

## Client application changes

The changes in this section apply to the applications that consume the API. Review each one against the client code before you deploy.

### Base endpoint change

The base endpoint moves from `/api/episerver/v3.0` to `/api/episerver/v4.0`. Update every API URL in your client applications.

### Site endpoint changes

The endpoint path `/site/` and the response header `x-epi-siteid` keep their version 3.x names. The underlying CMS model moves from `SiteDefinition` to `Application`, which changes the endpoint in three ways.

First, the single-item endpoint accepts a name instead of an identifier. Version 13.0 accepts a site name string at `GET /api/episerver/v4.0/site/{name}`. Version 3.x accepted a GUID at `GET /api/episerver/v3.0/site/{id}`. A request that carries a name with no match returns `404 Not Found` rather than `400 Bad Request`.

Second, version 13.0 removes the `id` property from the response. Use `name` to identify applications. The response takes the following shape:

```json
{
  "name": "My Site",
  "editLocation": "...",
  "contentRoots": { ... },
  "languages": [ ... ],
  "hosts": [ ... ]
}
```

Third, the `x-epi-siteid` header carries the application name as a string rather than a GUID.

Two further behaviors change in the response:

- **Content roots** – CMS 13 adds a `blueprints` content root. A blueprint is a reusable content structure in CMS 13, and the `blueprints` root holds those structures. The response always includes this root in `contentRoots`, whatever the value of the `IncludeInternalContentRoots` setting. Handle the extra entry in code that iterates the dictionary.
- **No wildcard hosts** – The response returns only the hosts that you configure explicitly. CMS 13 rejects a wildcard host. The validation error reads `Host cannot be a wildcard '*'. Make the application default instead.` Mark the application as default, then update client code that tests for a wildcard host.

### JSON serialization differences

System.Text.Json output differs from Newtonsoft.Json output in the following cases. Review each one against the parsing logic in your client applications.

- **Null handling** – By default, the response includes properties that hold `null`. Set `DefaultIgnoreCondition` to `JsonIgnoreCondition.WhenWritingNull` to omit them.
- **Number handling** – System.Text.Json applies stricter rules to number types. A value such as `1.0` serializes as `1` for an integer type.
- **String-to-number coercion** – System.Text.Json rejects a JSON string where a numeric type belongs. Newtonsoft.Json converted the JSON string `"99"` to the integer `99`. System.Text.Json throws a `JsonException` for the same value.

Custom date formats also need review, because System.Text.Json and Newtonsoft.Json apply them differently. Both libraries write ISO 8601 values by default, such as `2024-01-15T10:30:00Z`.

## Removed APIs

Version 13.0 removes two of the three APIs that shipped in version 3.x. Content Delivery API is the only one that remains.

### Content Definitions API and Content Management API

Content Delivery API 13.0 ships neither `EPiServer.ContentDefinitionsApi`, `EPiServer.ContentDefinitionsApi.Commerce`, nor `EPiServer.ContentManagementApi`. Remove the package references:

```xml
<!-- Remove -->
<PackageReference Include="EPiServer.ContentDefinitionsApi" Version="..." />
<PackageReference Include="EPiServer.ContentDefinitionsApi.Commerce" Version="..." />
<PackageReference Include="EPiServer.ContentManagementApi" Version="..." />
```

Remove the matching service registrations from `Startup.cs`:

```csharp
// Remove from ConfigureServices
services.AddContentDefinitionsApi(...);
services.AddContentManagementApi(...);
```

Version 13 adds no replacement for either API. Optimizely has no plans to upgrade them, so no version 13 equivalent of `EPiServer.ContentManagementApi` or `EPiServer.ContentDefinitionsApi` follows.

CMS 13 exposes content management through the CMS REST API at `/_cms/v1`, which `AddCms()` registers automatically. Treat that API as a separate integration, not a drop-in replacement. Account for two differences before you plan the work:

- **Authentication** – The CMS REST API supports the client credentials grant only. Content Management API also accepted the authorization code and resource owner password grants. Solutions that used either one need a new authentication design.
- **API surface** – The endpoints, routes, and payloads differ from Content Management API and Content Definitions API. Budget for rewriting the calls rather than repointing them.

For the endpoint reference and the authentication setup, see [Introduction to the CMS REST API](docId\:MM1lPFKxUy4D2kTAk9-T3).

### OpenID Connect scopes for the removed APIs

Remove the scopes for the removed APIs from your OpenID Connect configuration:

```csharp
// Remove these scopes from OpenIDConnectApplication configurations
ContentDefinitionsApiOptionsDefaults.Scope  // "epi_content_definitions"
ContentManagementApiOptionsDefaults.Scope   // "epi_content_management"
```

## OpenID Connect changes

### Update OpenID Connect packages

Update the OpenID Connect package references to version 13.0.0:

```xml
<PackageReference Include="EPiServer.OpenIDConnect" Version="13.0.0" />
<PackageReference Include="EPiServer.OpenIDConnect.UI" Version="13.0.0" />
```

### Protected module path change

CMS 13 moves the protected module root, the path that serves the Optimizely user interfaces, from `/EPiServer/` to `/Optimizely/`. CMS 13 serves the OpenID Connect interface from `/Optimizely/OpenIDConnect/` instead of `/EPiServer/OpenIDConnect/`. Update any application that references the path directly.

The same change applies to the CMS edit interface, which moves from `/EPiServer/CMS/Content/` to `/Optimizely/CMS/Content/`.

## Package version compatibility

Version 13.0 declares a supported range for each Optimizely dependency. The following table maps the versions across the two releases:

| Package                                 | Version 3.x     | Version 13.0                      |
| --------------------------------------- | --------------- | --------------------------------- |
| `EPiServer.ContentDeliveryApi.Core`     | 3.x             | 13.0.0                            |
| `EPiServer.ContentDeliveryApi.Cms`      | 3.x             | 13.0.0                            |
| `EPiServer.ContentDeliveryApi.Commerce` | 3.x             | 13.0.0                            |
| `EPiServer.Cms`                         | 12.x            | 13.0.2 up to, but excluding, 14.0 |
| `EPiServer.Commerce.Core`               | 14.x            | 15.0.0 up to, but excluding, 16.0 |
| `EPiServer.Forms`                       | 5.x             | 6.0.1 up to, but excluding, 7.0   |
| .NET                                    | 6.0             | 10.0                              |
| JSON serializer                         | Newtonsoft.Json | System.Text.Json                  |

The Commerce package carries its own version number, 15.0.0. The other Content Delivery API packages use 13.0.0.

## Troubleshooting

### Build error: `JsonSerializerSettings` not found

Your code references the version 3.x serializer configuration type. Replace `JsonSerializerSettings` with `JsonSerializerOptions`, and change the `using` directive from `Newtonsoft.Json` to `System.Text.Json`. See [Update serializer configuration](docId\:E87g81KtntgK-65scIk5D).

### Build error: `JObject` or `JToken` not found

Your code references the Newtonsoft.Json dynamic JSON types. Replace them with the System.Text.Json equivalents in the following table:

| Newtonsoft.Json          | System.Text.Json                              |
| ------------------------ | --------------------------------------------- |
| `JObject`                | `JsonDocument`, `JsonElement`, or `JsonNode`  |
| `JToken`                 | `JsonElement` or `JsonNode`                   |
| `JArray`                 | `JsonElement.EnumerateArray()` or `JsonArray` |
| `JObject.Parse(json)`    | `JsonDocument.Parse(json).RootElement`        |
| `JToken.FromObject(obj)` | `JsonSerializer.SerializeToElement(obj)`      |

### Build error: `SiteDefinitionModel` not found

Version 13.0 removes `SiteDefinitionModel`. Replace it with `ApplicationModel`. The related type `SiteDefinitionLanguageModel` stays as obsolete, so it raises a warning rather than this error. See [Update the renamed site types](docId\:E87g81KtntgK-65scIk5D).

### Runtime error: `JsonSerializerOptions must specify a TypeInfoResolver`

Your code creates `JsonSerializerOptions` manually and passes them to an ASP.NET Core formatter. Set the resolver on the options:

```csharp
options.TypeInfoResolver = new DefaultJsonTypeInfoResolver();
```

### Runtime error: `No service for type 'ServiceAccessor<Application>'`

CMS 13 does not register `ServiceAccessor<Application>` in the dependency injection container. Inside an HTTP request context, resolve the application through `IApplicationResolver`:

```csharp
var resolver = serviceProvider.GetRequiredService<IApplicationResolver>();
var currentApp = resolver.GetByContext();
```

Outside an HTTP request context, such as a background task, resolve it through `IApplicationRepository`:

```csharp
var repository = serviceProvider.GetRequiredService<IApplicationRepository>();
var app = repository.List().First();
```

Both interfaces also expose asynchronous members, `GetByContextAsync` and `ListAsync`. Prefer the asynchronous overloads in request handlers, which is the pattern that the Alloy sample templates follow.

### Content properties missing from the JSON response

The JSON response omits custom content properties, or returns them with PascalCase keys. System.Text.Json does not apply `DictionaryKeyPolicy` to `[JsonExtensionData]` dictionary keys. A property stored under a PascalCase key such as `MainContentArea` serializes as `MainContentArea` rather than `mainContentArea`. See [Set camelCase keys on custom property models](docId\:E87g81KtntgK-65scIk5D).
