---
title: Install and configure Optimizely Graph on your site
slug: cms-12/docs/install-and-configure-optimizely-graph-on-your-site
docTags: 
createdAt: 2026-08-10T15:55:56.000Z
---

Optimizely Graph is a powerful, unified content query and delivery service that lets you access and reuse content across Optimizely products through a single GraphQL API. It enables flexible data retrieval, high-performance search, and dynamic content delivery for both Optimizely CMS and custom applications.

This guide explains how to install and configure Optimizely Graph for your site. For a broader understanding of Optimizely Graph concepts, API structure, schema management, and advanced configurations, see the [Optimizely Graph documentation](docId\:aM0WpNsNXe_n26IhkX5sG)
.

## Install Optimizely Graph

Install `Optimizely.ContentGraph.CMS` in your Optimizely Content Management System (CMS) site to sync content types and content. In Visual Studio, go to **Tools > Nuget Package Manager > Package Manager Console**. At the console, enter *install-package Optimizely.ContentGraph.Cms*.

:::hint{type="info"}
**Note**
Optimizely supports packages for ASP.NET with the package versions >= 2.0.
:::

## Configure Optimizely Graph for your site

Depending on your version of .NET, different methods exist to configure Optimizely Graph. The process for the different versions involves entering settings into configuration files. For that reason, gather the following information:

- `GatewayAddress` – URL for the API gateway. This value should be set to `https://cg.optimizely.com` for all production environments.
- `AppKey` – Sent to you when the account was created and is the public key from your HMAC authentication pair.
- `Secret` – Sent to you when the account was created and is the secret key from your HMAC authentication pair.
- `SingleKey` – Sent to you when the account was created and is the public key you can use on the public site.
- `AllowSendingLog` – Flag that lets you send errors and warnings to the API gateway. This data helps the Support team investigate when something goes wrong. Defaults to `true`.

:::hint{type="info"}
**Note**
The configuration for Optimizely Graph is only needed when working with the CMS site locally. The configuration is already set in the Optimizely Digital Experience Platform (DXP) site, so do not include the configuration for the CMS site when deploying it to DXP through the PaaS portal.
:::

### Configure the sync package configuration in the .NET framework

For newer versions of the .NET platform, you can enter the settings into the `web.config` file in the `<appSettings>` section.

```xml
<add key="optimizely__contentgraph__gatewayaddress" 
     value="https://cg.optimizely.com" />

<add key="optimizely__contentgraph__appkey" 
     value="Sent to you when the account was created and is the public key from your HMAC authentication pair" />

<add key="optimizely__contentgraph__secret" 
     value="Sent to you when the account was created and is the secret key from your HMAC authentication pair" />

<add key="optimizely__contentgraph__singlekey" 
     value="Sent to you when the account was created and is the public key you can use on the public site" />

<add key="optimizely__contentgraph__allowsendinglog" 
     value="true" />
```

The older settings format is still supported, but is deprecated.

```xml
<add key="optimizely:gatewayaddress" 
     value="https://cg.optimizely.com" />

<add key="optimizely:query.appkey" 
     value="Sent to you when the account was created and is the public key from your HMAC authentication pair" />

<add key="optimizely:query.secret" 
     value="Sent to you when the account was created and is the secret key from your HMAC authentication pair" />

<add key="optimizely:query.singlekey" 
     value="Sent to you when the account was created and is the public key you can use on the public site" />

<add key="optimizely:allowsendinglog" 
     value="true" />
```

### Configure the sync package settings in ASP.NET Core

Open `appsettings.json` and add your settings to configure your account.

```json
{
  "Optimizely": {
    "ContentGraph": {
      "GatewayAddress": "https://cg.optimizely.com",
      "AppKey": "Sent to you when the account was created and is the public key from your HMAC authentication pair",
      "Secret": "Sent to you when the account was created and is the secret key from your HMAC authentication pair",
      "SingleKey": "Sent to you when the account was created and is the public key you can use on the public site",
      "AllowSendingLog": "true"
    }
  }
}
```

If you do not use the [Content Delivery API](docId\:IMsjx9oL2_RQKO-0HXY-g) in your CMS site, add the following to the `ConfigureServices(IServiceCollection services)` method of `Startup.cs`. Optimizely Graph depends on the Content Delivery API, which is the minimum configuration.

If you want to customize Content Delivery API, see the [configure Content Delivery API documentation](docId\:rEKDgayvgt9oP2aovJhO5).

```json
services.ConfigureContentApiOptions(o =>
{
    o.IncludeInternalContentRoots = true;
    o.IncludeSiteHosts = true;
    // o.EnablePreviewFeatures = true; // optional
});

services.AddContentDeliveryApi(); // required
// For further configurations, see:
// https://docs.developers.optimizely.com/content-cloud/v1.5.0-content-delivery-api/docs/configuration

services.AddContentGraph();

// The following is obsolete and is kept for compatibility
// services.AddContentGraph(_configuration);
```

If you already use the Content Delivery API in your application, you only need to add a line to register Optimizely Graph services.

```json
//other configurations for Content Delivery API

services.AddContentGraph();

//the following is obsolete and is kept for compatibility for now
//services.AddContentGraph(_configuration);
```

Set the Content Delivery options to `true` for the data related to site information to be synced to Optimizely Graph. Then you can query them through the `SiteDefinition` content-type.

```json
services.ConfigureContentApiOptions(o =>
  {
    o.IncludeInternalContentRoots = true;
    o.IncludeSiteHosts = true;
    //o.EnablePreviewFeatures = true;//optional
    //o.SetValidateTemplateForContentUrl(true);//optional
  });
```

- `IncludeInternalContentRoots` and `IncludeSiteHosts` – Set to `true` to sync information on content roots and site definitions to Optimizely Graph.
- `EnablePreviewFeatures` – Set to `true` if you want beta features in Content Delivery to be enabled for more data in Optimizely Graph.
- (Optional) `SetValidateTemplateForContentUrl` – Set to `true` to only provide URLs for content with CMS templates. If not, the URL is null.

## Property index modes

The CMS integration with Optimizely Graph has a feature to set property indexing modes. This can be done by adding an attribute to the property in your content type class. Optimizely supports typed models.

### `OutputOnly` mode

Not all content properties must be searchable or filterable when indexing Optimizely Graph content. You should turn off filtering and searching for these properties to reduce the load to Optimizely Graph and improve query performance.

To turn off searching and filtering for a property, set the indexing mode to `OutputOnly`. With this mode, the property value can still be queried and read from Optimizely Graph, but it is impossible to filter by that property using the [\`where\`](docId\:aAD2WsrvamPQvg8z_Ps5A) clause. It is also not searchable with [full-text search](docId\:sNcLLcLYgWv_QVb0Mtqus).

:::hint{type="warning"}
**Important**
To improve query performance, you should use `OutputOnly` mode on properties that you know you will not filter and search on.
:::

```csharp
[GraphProperty(PropertyIndexingMode.OutputOnly)]
public virtual string AStringProperty { get; set; }
```

### `Default` mode

With `Default` indexing mode, the property is stored in Optimizely Graph. This means that it can be filtered using the [\`where\`](docId\:aAD2WsrvamPQvg8z_Ps5A) clause, but it is not searchable with [full-text search](docId\:sNcLLcLYgWv_QVb0Mtqus).

```text
[GraphProperty(PropertyIndexingMode.Default)]
public virtual string AStringProperty { get; set; }
```

## Include inheritance

To get results from inherited types when querying a content type, include inheritance in the `ContentType` list. Set `IncludeInheritanceInContentType` to `true`.

```json
services.AddContentGraph(configureOptions: options =>
{
    options.IncludeInheritanceInContentType = true;
});
```

## Enable extract media type

To enable extracting data from the content media type, set `ExtractMedia` to `true`. Defaults to true.

```json
services.AddContentGraph(configureOptions: options =>
{
    options.ExtractMedia = false;
});
```

## Configure allowlist content

To set what content can be synced to Optimizely Graph and prevent syncing content with sensitive data, add the `Include` section to the `appsettings.json` file or use the configuration in `startup.cs`.

:::hint{type="info"}
**Note**
Configuration set in `startup.cs` overrides `appsettings.json`.
:::

`appsettings.json`

```json
{
  "Optimizely": {
    "ContentGraph": {
      "Include": {
        "ContentIds": [],
        "ContentTypes": []
      },
      "OnlySyncContentTypesInWhitelistToSchema": false
    }
  }
}

```

`startup.cs`:

```csharp
services.AddContentGraph(configureOptions: options =>
{
    options.Include.ContentIds = new int[] { 1, 2, 3 };
    options.Include.ContentTypes = new string[] 
    { 
        typeof(StandardPage).Name, 
        typeof(ProductPage).Name 
    };
});

```

- `ContentIds` – Array of content Ids (an array of integers) that syncs contents whose Ids exist in the `ContentIds` array and the content whose ancestor Ids exist in the `ContentIds` array.
- `ContentTypes` – Array of contentType names (an array of strings) that syncs content whose `ContentTypes` exist in the `ContentTypes` array.
- `OnlySyncContentTypesInWhitelistToSchema` – By default, content types are included in Optimizely Graph GraphQL's schema. If you want only the content types listed in `Include.ContentTypes` to be in the GraphQL schema, set `OnlySyncContentTypesInWhitelistToSchema` to `true`.

### Configuration options rules

- `ContentIds = []` and `ContentTypes = []` – Content is synced as normal.
- `ContentIds = []` and `ContentTypes != []`  – Only the content whose `ContentTypeID` matches in the `ContentTypes` or in the descendants that are inherited directly or indirectly from the `ContentTypes` are synced.

:::hint{type="info"}
**Note**
If the values in `ContentTypes` do not exist in CMS, you cannot sync content.
:::

- `ContentIds != []` and `ContentTypes = []` – Two types of content can be synced.
  - Content whose Ids exist in `ContentIds` are synced.
  - Content whose ancestor Ids exist in `ContentIds` are synced.

:::hint{type="info"}
**Note**
You cannot sync content if no content exists in `ContentIds`.
:::

- `ContentIds != []` and `ContentTypes != []` – Two types of content can be synced.
  - Content whose Ids exist in `ContentIds` and `ContentTypeID` matches in the `ContentTypes` or in the descendants that are inherited directly or indirectly from the `ContentTypes` are synced.
  - Content whose ancestor Ids exist in `ContentIds` and whose `ContentTypeID` matches in the `ContentTypes` or in the descendants that are inherited directly or indirectly from the `ContentTypes` are synced.

:::hint{type="info"}
* **Note**
  When content is synched, language versions of that content are available in Optimizely Graph.
* Content (that is linked to synced content) is synched within that synced content, regardless of any allowlist conditions that otherwise apply.
* When querying contents in GraphiQL, you can only directly query the content types that exist in the `ContentTypes` array and their descendants that are inherited directly or indirectly from the `ContentTypes` array.
:::

## Configure sync version content

```json
"Optimizely": {
    "ContentGraph": {
       "ContentVersionSyncMode": "DraftAndPublishedOnly"
    }
}
```

To determine which versions content can be synced to Optimizely Graph, you have the following options:

- `DraftAndPublishedOnly` (Default) – To sync the published content and draft content.
- `PublishedOnly` – To sync only the published content.
- `All` – To sync all status version content.

## Configure resync of referenced content

When content is referenced by other content using `ContentLink`, `ContentReference`, `LinkItem`, or `ContentArea`, modifying the content can trigger the referencing content's resyncing to Optimizely Graph.

To enable this, set `SyncReferencingContents` to `true` in `appsettings.json`. The default value is `false`.

To enable resync of referencing content, set `SyncReferencingContents` to `true` and ensure `EventIndexingOptions.Enable` is set to `true`. `Configuring ReferenceTypesToReIndex` or `IndexReferencingContent` is optional and only required if you want to control or limit which types of referencing content are resynced.

If the number of referencing content items is large, resyncing may take a long time after the related content is updated. In that case, configure the setting to `false` and wait for Optimizely Graph’s scheduled sync job to run again.

```json
"Optimizely": {
    "ContentGraph": {
       "SyncReferencingContents": "false"
    }
}
```

## Configure resync of related content

You can specify which referencing content is resynced based on the reference type. Depending on the different sites, you can change the settings to reduce the number of resynced content.

Types of references

- `PropertyContentArea`
- `PropertyContentAreaItem`
- `PropertyContentReference`
- `PropertyContentReferenceList`
- `PropertyLinkItem`
- `PropertyLinkCollection`
- `PropertyPageReference`

To configure the setting, in `startup.cs` when configuring `EventIndexingOptions`, set the setting `ReferenceTypesToReIndex`.

The following example states that when a page is updated, only pages that reference this page through `ContentArea` or `ContentAreaItem`  properties are resynced:

```csharp
services.Configure<EventIndexingOptions>(ops =>  
{
   ops.ReferenceTypesToReIndex = new List<Type>()
        {
            typeof(PropertyContentReference),
            typeof(PropertyContentAreaItem)
        };
});
```

With this configuration, only Page A and Page B are resynced, Page C and Page D are not resynced after Block X is updated.

![](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/MjLDIOBdNqyuicDPhbgyO_a2608640af02dfbecaa614d83291d01784abf302db2d022ecb962a7315409c1c-image.png)

If `ReferenceTypesToReIndex` is not changed, all related contents is resynced, regardless of the type of the reference property.

Another way to configure this is through `appsettings.json`, by setting `IndexReferencingContent`. The following are the three possible values:

- `None` – Related content is not resynced.
- `All` – All related content is resynced.
- `Inlined` – Only content with references that cause the referenced content to be inlined are resynced (`ContentArea`, `ContentAreaItem`).

When `IndexReferencingContent` is configured, the `ReferenceTypesToReIndex` settings are ignored.

```json
"Optimizely": {
  "ContentGraph": {
    "EventIndexing": {
      "IndexReferencingContent": "Inlined"
    }
}
```

## Disable inline content in the expanded field of reference properties

You can expand reference properties in the range of 0-5 for all reference types. The default is `1`, and for `ContentArea`, it is `3`. To disable expanding entirely, set the `ExpandLevel` to `0`.

Example 1: Prevent expanding all reference types except `ContentArea`.

```csharp
services.AddContentGraph(options =>  
{  
    options.ExpandLevel.Default = 0;  
});
```

Example 2: Prevent expanding all reference types, including `ContentArea`.

```csharp
services.AddContentGraph(options =>  
{  
    options.ExpandLevel.Default = 0;  
    options.ExpandLevel.ContentArea = 0;  
});
```

If your site does not need to inline content in reference fields like `ContentReference` or `LinkItem`, setting the depth of expansion to `0` speeds up sync and reduces document size.

## Configure sync of content type when the site starts

Open `Startup.cs` and add the following:

```csharp
services.Configure<EventIndexingOptions>(ops => 
{
  ops.SyncContentTypesOnInit = true;
});
```

## Configure to enable preview tokens for the on-page editing feature

When calling `AddContentGraph`, you can enable preview tokens for the on-page editing feature by setting `EnablePreviewTokens` to `true`.

You can also set the `OnGeneratingPreviewUrl` event to customize your content preview URL before adding the preview token as a query string parameter.

You can enable preview tokens in your `Program.cs`,`Startup.cs`, or `appsettings.json`.

See [On-page editing and Optimizely Graph](https://docs.developers.optimizely.com/platform-optimizely/v1.4.0-optimizely-graph/docs/on-page-editing-using-content-graph).

```csharp
services.AddContentGraph(options =>
{
	options.EnablePreviewTokens = true;

  options.Events.OnGeneratingPreviewUrl = ctx =>
  {
    // ctx.UpdateUrl(new Uri("/test?id=" + ctx.ContentReference, UriKind.RelativeOrAbsolute));
    return Task.CompletedTask;
  };
});
```

```json
"Optimizely": {
    "ContentGraph": {
       "EnablePreviewTokens": "true"
    }
}
```

## Configure the format for `XhtmlString`

You can fetch the rich text stored in `XhtmlString` in a different structured delivery format than a raw HTML string.

By default, `XhtmlString` is returned as a single string containing the raw HTML.

```csharp
services.Configure<ContentApiOptions>(op =>
{
    op.RichTextFormat = RichTextFormat.Html;
});
```

Optimizely Graph implements headless CMS. HTML formatting is specific to the web and not headless. So you should use the `RichTextFormat.Structured` JSON format for rich text fields and then implement your frontend to render the rich text to your target platform (mobile, web, Windows, or Mac applications).

:::hint{type="info"}
**Note**
`RichTextFormat.Html` and `RichTextFormat.HtmlAndStructured` has some limitations and unreliability when a block is added to the rich text fields.
:::

Optimizely Graph query.

```graphql
 StandardPage {
    items {
      Name,
      MainBody 
   }
}
```

Response example.

```text
 "MainBody": "<p><strong>The weather is very nice !</strong></p>"
```

### Structured HTML

To return `XhtmlString`as structured HTML, use the following:

```csharp
services.Configure<ContentApiOptions>(op =>
{
    op.RichTextFormat = RichTextFormat.Structured;
});
```

Optimizely Graph query.

```graphql
StandardPage {
  items {
    Name
    MainBody {
      Structure
    }
  }
}

```

Response example.

```javascript
{
  "MainBody": {
    "Structure": "{\"type\":\"richText\",\"children\":[{\"type\":\"paragraph\",\"children\":[{\"text\":\"The weather is very nice !\",\"bold\":true}]}]}"
  }
}

```

### Raw and structured HTML

You can return the raw HTML and the structured HTML.

```csharp
services.Configure<ContentApiOptions>(op =>
{
    op.RichTextFormat = RichTextFormat.HtmlAndStructured;
});
```

Optimizely Graph query.

```graphql
 StandardPage {
    items {
      Name,
      MainBody {
         Structure
         Html
      }
   }
}
```

Response example

```javascript
{
  "MainBody": {
    "Structure": "{\"type\":\"richText\",\"children\":[{\"type\":\"paragraph\",\"children\":[{\"text\":\"The weather is very nice !\",\"bold\":true}]}]}",
    "Html": "<p><strong>The weather is very nice !</strong></p>"
  }
}
```

:::hint{type="info"}
- **Note**
  Changing the `ContentApiOptions.RichTextFormat` configuration requires you to re-run the [sync job](docId\:Tei3cE_-YAK7nDyoveqDy) .
- The [HTML elements](https://github.com/ianstormtaylor/slate/blob/main/site/examples/ts/paste-html.tsx) that Optimizely has seen a mapping for are mapped, but HTML elements that Optimizely has not seen a mapping for are returned unmapped (the HTML element name).
:::

### Configuration to retrieve structure as JSON

By default, this feature is disabled. When enabled, the structure's response is similar to the following:

```text
"MainBody": {
  "Structure": {
    "type": "richText",
      "children": [
        {
          "type": "paragraph",
          "children": [
            {
              "text": "Test"
            }
          ]
        }
      ]
  },
  "Html": "<p>Test</p>"
}
```

To enable this feature, complete the following:

1. The `RichTextFormat` configuration must be `Structured`or `HtmlAndStructured` (see previous).
2. Add the `RichTextStructAsJson` setting.

```json
"Optimizely": {
    "ContentGraph": {
       "RichTextStructAsJson": true
    }
}
```

3. Run the indexing job.

:::hint{type="info"}
**Note**
The default type for `structure` is `string`. But when using this feature, its type changes to `object`. You must update your frontend site to handle both types for the `structure` field of the `XhtmlString`.
:::

## Configure `Date`  to `DateTime`

By default, the `DateTime` type in the GraphQL schema is `Date`. You can change the type to `DateTime` by setting `UseDateTimeGQLType` to `true`.

```json
"Optimizely": {
    "ContentGraph": {
       "UseDateTimeGQLType": true
    }
}
```

:::hint{type="info"}
**Note**
This configuration is not applied to the `_modified` field. It is always `Date`.
:::

## Configuration to prevent field collision

By default, this feature is disabled. To enable it, complete the following:

1. Change the `PreventFieldCollision` setting.

```json
"Optimizely": {
    "ContentGraph": {
       "PreventFieldCollision": true
    }
}
```

2. Reset the account and run the indexing job. See [Smooth rebuild](docId\:s6ys28fS7jAv_tD_uKfzb).

:::hint{type="info"}
**Note**
This feature only works in versions >= 3.7.0.

When this feature is enabled and you want to change the `DateTime` type (from `Date` to `DateTime` or vice versa), you must run the indexing job again. While the job is running, the datetime fields may return null.
:::

## Configure required fields

In CMS, you can set properties to be required. This means that a value must be entered. You can get information from the GraphQL schema about which fields always have a value. See [Property attributes](docId\:YkUaZBu_-HkFE3ZzI0p0F).

To enable this feature, complete the following:

1. Configure CMS to send metadata about required properties. In `startup.cs` add the following:

```csharp
services.AddContentGraph(configureOptions: options =>
{
   options.MarkRequiredPropertiesToGraph = true; //default is false
});
```

2. Set the `Required` attribute for the property. An example is in the [Property attributes documentation](docId\:YkUaZBu_-HkFE3ZzI0p0F).

The GraphQL query is now configured to know that the field always has a value. For information, see the blog on [Required fields support in Optimizely Graph](https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2024/9/optimizely-graph-release-3-11-0/).
