For Optimizely and .NET teams

Optimizely CMS, DXP, AI editor tools, and technical SEO

Practical solutions, open-source tools, and field-tested code to help you solve real Optimizely challenges and ship with confidence.

Latest posts

All posts →

Friday, 2 October 2026

From robots.txt to GEO Analytics: A Year On

In September 2025, I wrote about updating your robots.txt file to explicitly allow AI bots like GPTBot, ClaudeBot, PerplexityBot, and OAI-SearchBot. Back then, the main challenge was simply granting access: deciding whether to allow training crawlers, search indexing bots, or a mix of both.

A year later, the conversation has shifted. Allowing bots in robots.txt is only table stakes. The real question today is: once you let them in, how often do they visit, what are they reading, and does that traffic convert into actual user referrals? This discipline is known as Generative Engine Optimization (GEO), and Optimizely CMS 13 now builds native tools to measure and optimize for it.

The Evolution from Access to Measurement

Allowing an AI user-agent in robots.txt tells an LLM crawler it is legally and technically welcome. What it does not tell you is whether the crawler understood your content structure, indexed your canonical answers, or simply scrapped your raw HTML without citing your brand.

Here is what the shift looks like in practice:

  • 2025 (Permission): Defining user-agent rules in robots.txt to prevent AI engines from silently bypassing your site.
  • 2026 (Visibility & Analytics): Tracking crawl frequency, measuring referral rates, and observing which specific pages AI platforms extract data from.
  • 2026 (Optimization): Structuring content specifically for AI ingestion using dedicated schema, Q&A fields, and entity models.

Tracking AI Crawlers with GEO Analytics & Agent Visibility

In Optimizely Reporting and Optimizely Analytics, the platform tracks incoming bot traffic categorized specifically by AI engine rather than burying them inside generic organic traffic numbers.

The core metric introduced here is the Crawl-to-Refer Ratio:

Metric State What It Means Action Required
High Crawl-to-Refer Ratio Bots crawl your pages frequently, but AI platforms rarely cite your site in user answers. Improve content structure, add clear Q&A summaries, and use explicit schema so AI models can easily attribute answers.
Low Crawl-to-Refer Ratio Visits are proportional to references and citations in generative responses. Identify your top-performing content templates and replicate their layout across other sections.

Note: While earlier preview documentation referred to the "GEO Analytics dashboard", newer environments direct teams to the Agent Visibility dashboard inside Optimizely Analytics. This view enriches crawler hits with request intent and Optimizely Opal / Mark facet insights.

Native AI Optimization Features in CMS 13

Beyond tracking bots, Optimizely CMS 13 introduces dedicated agents and metadata models to help content rank in generative engine answers:

1. Schema and Answers Agent

Generative search engines prefer extractable, atomic facts over sprawling multi-paragraph layouts. The Schema and Answers Agent automatically evaluates content relationships and suggests structured metadata, FAQ schemas, and direct Q&A blocks so LLMs can cleanly parse answers.

2. GEO Recommendations Agent

Similar to traditional on-page SEO checkers, this agent runs checks against your draft content in Visual Builder, evaluating your pages against known generative search visibility signals before publishing.

3. Optimizely Graph Metadata Integration

Because Optimizely Graph indexes the composition model of your pages and blocks, it surfaces structured metadata contracts (such as _itemMetadata and _assetMetadata) directly. When AI aggregators and agents query your GraphQL endpoint or rendered pages, the data schema is unambiguous.

Updating Your Strategy: What to Do Next

  1. Audit your current robots.txt: Verify that recent AI crawlers are still permitted. If you used the balanced template from last year (allowing search bots while blocking training crawlers), verify that your directives reflect current user-agent names.
  2. Review the Agent Visibility Dashboard: Check which pages have the highest crawl volume from OpenAI and Perplexity. Look for high crawl counts paired with low citation referrals.
  3. Add Structured Q&A Blocks: Update key commercial and technical documentation pages with concise summary definitions and FAQ schema markup.
  4. Leverage SEOBOOST add-on: If you are managing technical metadata, canonical tags, and robots directives on Optimizely CMS, use automated metadata tooling so headers and crawler rules stay aligned across multi-site instances.

Important

  • Do not rely entirely on robots.txt for compliance. Some rogue or emerging AI scrapers ignore standard robots directives. For strict enforcement, pair your robots.txt file with Web Application Firewall (WAF) bot rules on Optimizely DXP or Cloudflare.
  • The legacy GEO Analytics dashboard was grandfathered for early access instances. Modern CMS 13 setups use the Agent Visibility dashboard in Optimizely Analytics for richer crawler telemetry.
  • Generative search engines value freshness and canonical accuracy. If your content updates frequently, ensure your XML sitemap and Optimizely Graph index sync without delay so bots do not reference stale answers.

Links

October 02, 2026 →

Thursday, 1 October 2026

Why Your CMS 12 and CMS 13 Sites Can't Share a Graph Instance

If you're planning your Optimizely CMS 13 upgrade and you already run Optimizely Graph on your CMS 12 site, there's one detail that catches almost everyone off guard: you cannot point your CMS 13 environment at the same Graph instance your live CMS 12 site is using. Not temporarily, not just for testing. CMS 13 ships a breaking change to the Graph schema, and a CMS 12 site and a CMS 13 site cannot share a Graph instance without migrating the schema first.

This isn't an optional step you can defer. Graph is required to use CMS 13's full capabilities, including Content Manager and Experiences, so for any site already running Graph on CMS 12, migrating it is part of the CMS 13 upgrade itself, not a follow-up task.

Why This Happens

CMS 13 changes what Graph indexes and how it's shaped. Experiences, Sections, and Visual Builder's composition model change the object graph that gets synced from your CMS content tree, so the schema CMS 13 expects Graph to serve is different from the one CMS 12 populates. Point a CMS 13 site at a CMS 12 Graph instance (or vice versa) and queries either fail outright or quietly return the wrong shape of data.

That's why the upgrade guidance is blunt about it: treat the Graph migration as a required part of moving to CMS 13, not a nice-to-have cleanup step you can do later.

Two Ways to Handle the Migration

You have two supported paths. Which one you pick depends mostly on whether your site is live and how much downtime you can tolerate.

Option 1: Provision a New Graph Instance (Recommended, Zero Downtime)

Your live CMS 12 site keeps using its existing Graph instance while your CMS 13 deployment slot uses a brand new one. Graph never goes down for either version.

  1. Log in to the DXP Management Portal at https://paasportal.episerver.net.
  2. Open your organization and project, then go to the API tab and open Optimizely Graph Service Configurations. Click Prepare for CMS 13 to provision a separate Graph instance.
  3. Deploy CMS 13 through a deployment slot. The slot uses the new Graph credentials while production keeps using the old ones.
  4. Validate the slot against the CMS 13 schema without touching your live CMS 12 Graph data.
  5. Swap the slot into production. Production automatically picks up the new CMS 13 Graph credentials.

The old CMS 12 Graph instance isn't deleted immediately. It's retired in two stages so anything still pointing at the old keys has time to surface:

  • Stage 1 - Key rotation (soft delete): runs once all linked front-end environments are confirmed on the new key and a grace period (default seven days) has elapsed since that confirmation — or, if there are no linked front-end environments, seven days after go-live. This disables the old keys, so anything still using them starts failing visibly.
  • Stage 2 - Hard delete: the old Graph instance is removed a further 30 days after Stage 1.

That visible failure in Stage 1 is a feature, not a bug. It's your forcing function for finding every forgotten front end, webhook, or scheduled job that still has the old single key hardcoded somewhere.

Option 2: Smooth Rebuild on Your Existing Instance (Downtime Required)

If you'd rather keep your existing Graph instance and credentials (so external consumers never need to update anything), you can migrate the schema in place instead. This requires taking the site offline.

  1. Put the site behind a static maintenance page.
  2. In CMS, go to Settings > Smooth Rebuild, open Smooth Rebuild Settings, and click Start.
  3. Graph creates a new deployment slot (green) alongside the current one (blue) and starts syncing content into it.
  4. Test the green slot before promoting it. Use the Optimizely Graph playground (GraphiQL) with the New toggle enabled, or add the cg-query-new: true header to your GraphQL requests to target it directly.
  5. Deploy CMS 13 through a deployment slot once the rebuild has finished syncing.
  6. Promote the green slot to live.
cg-query-new: true

Because the CMS 13 schema change is breaking, you can't use Graph's normal zero-downtime read-only or read-write slot modes here. The site has to be fully offline behind the maintenance page for the duration of the rebuild.

Important

  • If you haven't implemented Optimizely Graph on your CMS 12 project at all, none of this applies to you. Follow the standard DXP upgrade path through Integration, Preproduction, and Production.
  • Run whichever path you choose in Integration and Preproduction first. You need to know how long the sync or rebuild actually takes before you size a Production maintenance window around it.
  • Audit anything with a hardcoded Graph single key (Next.js front ends, webhooks, scheduled jobs) before you start. Key rotation will break them loudly, which is useful, but you'd rather know in advance than get paged for it.
  • Local development config (GatewayAddress, AppKey, Secret, SingleKey in appsettings.json or web.config) is separate from what DXP auto-provisions on deployment. Don't carry your CMS 12 Graph configuration into your CMS 13 deployment and assume it'll just work.

Which Option Should You Pick?

Situation Go with
Site is live in production and downtime isn't acceptable Option 1: new Graph instance
Site isn't live yet, or a maintenance window is acceptable Option 2: smooth rebuild
Several external consumers have the single key hardcoded Option 1: forced key rotation surfaces every stale config for you
You want zero changes for external Graph consumers Option 2: credentials never change

Either way, don't treat this as a side detail of the CMS 13 upgrade. It's the part of the migration most likely to take down search, navigation, or personalization on your site if you discover it mid-deployment instead of during planning.

Links

October 01, 2026 →

Thursday, 13 August 2026

Optimizely XML Resource Builder: A Visual Studio Extension for Language Resource Files

Anyone who has worked on a larger Optimizely solution will know the feeling. You add or update a page type, block type, or shared base class, and then need to keep the language resource XML in sync. It is not difficult work, but it is repetitive, easy to miss, and becomes more painful as the number of content types grows.

This started as a small pet project for my own convenience. I wanted a quick way to generate a starting point for Optimizely language resource files directly from the content type code already in a project. Once it was useful in day-to-day work, I thought it could also benefit the wider Optimizely community if I packaged it properly and made it publicly available.

That is where Optimizely XML Resource Builder came from.

What it does

Optimizely XML Resource Builder is a Visual Studio extension that scans the project you select and generates an Optimizely language resource file from its C# content type definitions.

It reads content type attributes such as ContentType and SiteContentType, then creates the XML entries for the content type name, description, properties, captions, and help text.

The default output is:

Resources\Translations\ContentTypes-en.xml

You can change both the language code and output folder from the extension dialog.

Why I built it

Language resource files are useful for keeping the Optimizely editor UI clean and consistent, especially when you are working across several languages. The challenge is that the information is usually already present in code:

[ContentType(DisplayName = "Article Page")]
public class ArticlePage : SitePageData
{
    [Display(Name = "Heading", Description = "The main page heading")]
    public virtual string Heading { get; set; }
}

Copying those values into XML by hand adds another maintenance step. The extension uses the information already available in the attributes and gives you a generated file to review, commit, and translate.

Key features

  • Project-level command: right-click an Optimizely project in Solution Explorer to generate the resource XML.
  • Flexible content type detection: scan for SiteContentType, ContentType, or any additional attributes you configure.
  • Configurable generation dialog: choose a comma-separated attribute list, language code, excluded property prefixes, and output folder before generating the file.
  • Caption casing control: keep the original display-name case or convert captions to sentence case while preserving uppercase acronyms.
  • Overwrite control: choose whether to replace the existing XML file or safely add only missing entries.
  • Useful XML generated from code: create content type names, descriptions, property captions, and help text from the attributes already defined in C#.
  • Shared base-class support: find properties on custom base classes and add them under icontentdata.
  • Safe updates: add only missing XML entries by default, preserving existing translations and custom values. Enable overwrite only when you want a fresh file.
  • Solution-specific preferences: save settings per solution, rather than using one global set of values across every project.

How to use it

  1. Install the extension.
  2. Open an Optimizely solution in Visual Studio.
  3. In Solution Explorer, right-click the project that contains the content type classes.
  4. Select Build Optimizely XML Resource File.
  5. Set the attribute list, language code, output folder, and any exclusions.
  6. Select OK.

If no matching content types are found, the extension shows a warning and does not create an empty XML file.

Example output

<contenttypes>
  <icontentdata>
    <properties>
      <metadescription>
        <caption>Meta description</caption>
      </metadescription>
    </properties>
  </icontentdata>
  <articlepage>
    <name>Article Page</name>
    <properties>
      <heading>
        <caption>Heading</caption>
        <help>The main page heading</help>
      </heading>
    </properties>
  </articlepage>
</contenttypes>

Compatibility

The extension supports Visual Studio 2022 and later.

Links

I hope it saves other Optimizely developers the same repetitive work it saves me. If you try it, feedback and contributions are very welcome.

August 13, 2026 →

Tuesday, 21 July 2026

Advanced Task Manager Gets a Big Update for Optimizely CMS

Optimizely's content approval workflow works well when you are approving one item at a time. The problem starts when you are managing a large queue across pages, blocks, and media. Once the list grows, finding the right items, approving them in bulk, or publishing them in a controlled way becomes much more time-consuming than it should be.

This update focuses on solving those real editorial workflow problems. Every feature below came from day-to-day use of the tool in larger Optimizely solutions where too many clicks, too much scrolling, or missing workflow controls were slowing editors down.

Related posts: CMS 12 release post | Legacy CMS 11 post

Advanced filtering

The task list now includes a proper filter bar, making it easier to work with larger approval queues.

  • Status - switch between In Review and Ready to Publish.
  • Type - filter Pages, Blocks, or Assets/Media.
  • Site - narrow the list to one site in multi-site solutions.
  • Content search - search by content ID or part of the content name.

Each filter updates the URL, so filtered views can be bookmarked or shared with other editors.

Note: Active filters appear as removable badges below the filter bar, and a Clear all option removes everything at once.

Select all across pages

The existing table checkbox used to select only the current page of results. This update adds support for selecting all matching items across all pages.

If more results exist than are visible on the current page, a banner appears and lets the editor load every matching approval ID into the current selection. That means bulk approval can now cover the full filtered result set in one action.

This selection respects the active filters, so editors can safely approve only the tasks they intended to include.

Scheduled publishing

Publishing immediately after approval is still supported, but it is no longer the only option.

When Publish selected content after approval is enabled, editors can now choose to schedule publishing for a specific future date and time. The selected datetime is passed into Optimizely's normal publishing pipeline using IVersionable.StartPublish, so standard scheduled publishing behavior is used without any custom publishing job.

Approve blocks and media used by a page

This is one of the most useful additions in the release.

Sometimes a page is ready to go live, but the blocks or media it references are still waiting in their own approval queues. Instead of finding those items one by one, editors can now use Approve Page Dependencies to approve pending block and media dependencies for a selected page in one action.

  1. Open Approve Page Dependencies from the filter bar.
  2. Select a page from the hierarchy tree, or enter the content ID directly.
  3. Choose one or more language branches.
  4. Optionally enter an approval comment.
  5. Optionally publish approved dependencies immediately.
  6. Run the action and review the result count.

Behind the scenes, the tool inspects ContentArea and ContentReference properties on the selected page, gathers referenced blocks and media, and approves the items that still have pending approval steps.

Note: This action approves only the dependencies referenced by the page. It does not approve the page itself.

Task ordering

The task list columns are now sortable. Editors can sort by content name, content type, task type, submission date, who started the review, or deadline if the deadline property is enabled.

This sounds small, but it makes a big difference when triaging long queues and trying to prioritize urgent work.

Site column for multi-site solutions

In multi-site installations, the list now includes a Site column so editors can immediately see which site each task belongs to. In single-site environments, the column stays hidden.

This works especially well with the Site filter when approval queues need to be reviewed site by site.

Version availability

As of July 24, 2026, these features are available in:

  • Version 4.1.0 for Optimizely CMS 13 / .NET 10
  • Version 3.1.0 for the CMS 12 branch

Change Approval support is currently available only in the CMS 12 branch, because EPiServer.ChangeApproval does not yet have a CMS 13-compatible release.

Install or update

dotnet add package AdvancedTaskManager

Links

GitHub repository
NuGet package

Conclusion

This release makes Advanced Task Manager much more useful for teams handling larger approval volumes across pages, blocks, media, sites, and languages. The focus here is less about adding features for their own sake and more about making approval workflows faster, clearer, and easier to control in real Optimizely projects.

If you run into issues or want to suggest further improvements, the source code, changelog, and issue tracker are available on GitHub.

July 21, 2026 →

Friday, 10 July 2026

Optimizely DXP: Every Supported Culture, One Searchable Page

Quick one for anyone building multi-language sites on Optimizely DXP. I put together a reference tool listing all 806 supported cultures. More usefully, it shows which ones actually work with Azure Translator's 1-click auto-translate, and which get full NLP treatment (stemming, tokenization, decompounding) from Optimizely Graph.

If you've ever added a new market and then spent twenty minutes digging through docs to figure out whether editors will get auto-translate for that locale, or whether search will actually work well in that language, this is for that exact moment. Search or filter by culture name or code, grab the exact culture code you need for config, and see right away which capabilities you're getting.

806 cultures. 33 with Azure Translator support. Full Graph NLP coverage mapped out. All in one table.

Check it out here: Optimizely DXP – Supported Cultures / Languages

July 10, 2026 →

Tuesday, 3 March 2026

OpenAI-Driven AI Assistant for TinyMCE in Optimizely CMS 12

The Tiny.AI add-on enhances Optimizely CMS 12 by seamlessly integrating OpenAI directly into the TinyMCE editor. It empowers editors to rewrite, improve, summarize, expand, or translate selected content without leaving the CMS. Instead of copying content into external AI tools and risking formatting issues, Tiny.AI processes HTML safely and returns clean, CMS-ready output.

Installation

The command below will install the add-on in your Optimizely project.

dotnet add package A2Z.Optimizely.Tiny.AI

Configuration

Add your OpenAI configuration inside appsettings.json. The model value below is an example and can be updated to a supported model that fits your setup.

{
  "OpenAI": {
    "ApiKey": "YOUR_API_KEY",
    "Model": "gpt-4o-mini"
  }
}

The module automatically registers required services, the API controller, and the TinyMCE plugin.

Service Registration

The package registers the OpenAI service, circuit breaker, and authorization services automatically through an initialization module.

[ModuleDependency(typeof(InitializationModule))]
public class AiInitialization : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Services.AddHttpClient<IOpenAiService, OpenAiService>();
        context.Services.AddSingleton<EditorAuthorizationService>();
        context.Services.AddSingleton<SimpleCircuitBreaker>();
    }
}

Usage

Once installed and configured, editors will see a new AI Assistant button inside the TinyMCE toolbar.

TinyAI Icon

How it works:

  1. Select content inside the TinyMCE editor.
  2. Click the AI Assistant button.

  3. Choose an action (Rewrite, Improve, Summarize, Expand, Translate).

  4. Optionally specify a language.
  5. Apply changes and the HTML is replaced instantly.

The AI processes the selected HTML and returns valid HTML only, preserving links and formatting.

Security

Access to Tiny.AI is restricted to users in the following roles:

  • CmsEditors
  • Administrators

Authorization is validated server-side to ensure only permitted users can invoke AI functionality.

Resilience & Performance

Tiny.AI is built for production environments and includes:

  • Retry logic for transient API failures
  • Exponential backoff for rate limiting (429 responses)
  • Circuit breaker protection after repeated failures
  • Structured logging for monitoring
  • Token usage tracking
  • Estimated cost calculation per request

If multiple consecutive failures occur, the circuit breaker temporarily disables AI calls to protect system stability.

Cost Transparency

Each request returns:

  • Prompt tokens
  • Completion tokens
  • Total tokens
  • Estimated cost

This provides visibility into AI usage and allows teams to monitor spending effectively.

API Endpoint

POST /api/editor/ai/action

Example request:

{
  "action": "rewrite",
  "html": "<p>Some content</p>",
  "language": "en"
}

Example response:

{
  "html": "<p>Rewritten content...</p>",
  "tokens": 512,
  "cost": 0.0003
}

Compatibility

Tiny.AI currently supports Optimizely CMS 12 only. Check the GitHub repository or package page for the latest updates and version support.

Why Tiny.AI?

AI is becoming an essential productivity tool for content teams. Tiny.AI integrates OpenAI directly into Optimizely CMS workflows while maintaining security, HTML integrity, and operational resilience.

It eliminates the need for external tools and keeps the editorial workflow fast, clean, and fully integrated.

You can access the code and documentation for Tiny.AI on its GitHub repository.

March 03, 2026 →

Thursday, 12 February 2026

Advanced Form Submissions for Optimizely CMS

Advanced Form Submissions is an alternative submissions dashboard for Optimizely CMS 12 built for teams that need better control over Optimizely Forms data. It improves the default submission view with stronger filtering, export tools, bulk actions, and a more practical review experience for editors and administrators.

In larger Optimizely solutions, form data quickly becomes harder to manage across multiple sites, languages, and forms. This add-on gives teams a faster and more flexible way to review submissions without relying on the default interface alone.

Why use Advanced Form Submissions

  • Filter submissions by site, language, and form.
  • Search across submission content and apply date range filters.
  • Export filtered data as CSV, XML, or JSON.
  • Delete submissions more efficiently.
  • Open the original form directly from the dashboard.
  • Preview a submission on the original page with form hydration.

Key features

Personalized dashboard

The dashboard is designed for day-to-day editorial use and supports a more flexible review workflow.

  • Reorder columns to fit your needs.
  • Hide fields that are not useful for your review process.
  • Save settings per user, form, and language.
  • Use paged data loading for better performance on large datasets.

Advanced filtering and search

Submissions can be narrowed down quickly with filters for site, language, and form, plus free-text search and from/to date filtering.

Export and management tools

  • Export filtered submissions as CSV, XML, or JSON.
  • Delete selected or filtered submissions.
  • Jump directly to the related Form Container Block in CMS.

Better data display

  • File upload values are shown as clickable links.
  • Selection fields such as checkboxes, radio buttons, and dropdowns are rendered more clearly during review.

Quick view of an Optimizely form submission in Advanced Form Submissions

View submission on page

The add-on supports form hydration, which lets authorized users open a submitted form directly on the original page with the submitted values prefilled.

  • Open a submission in its original page context.
  • Review how the form looked when it was submitted.
  • Keep the workflow permission-aware and security-aware.

Advanced Form Submissions dashboard in Optimizely CMS

Advanced Form Submissions access from the Optimizely CMS form block

How to access it

The dashboard is available in two ways:

  • From the CMS global menu as Form Submissions.
  • From a Form Container Block through the custom Advanced Form Submissions view.

Installation

Install the package from the Optimizely NuGet feed:

dotnet add package A2Z.Optimizely.AdvancedFormSubmissions

Register the add-on during application startup:

using AdvancedFormSubmissions;

builder.Services.AddAdvancedFormSubmissions();

This registration adds the module, protected resources, CMS integration, and the default authorization policy.

Authorization and security

The add-on uses a dedicated authorization policy: form:submissions.

By default, access is granted to:

  • CmsAdmins
  • WebAdmins
  • Administrators

The same policy protects menu visibility, dashboard access, and form hydration behavior.

Compatibility

As of July 24, 2026, this add-on supports Optimizely CMS 12.

Limitations

Encrypted Optimizely Forms fields are intentionally not supported for dashboard display or hydration.

  • Encrypted values cannot be read through public APIs.
  • Encrypted fields are not hydrated on the front end.
  • Exports may show empty or masked values for encrypted fields.

Links

GitHub repository
README
NuGet package

FAQ

Does this replace Optimizely Forms?
No. It improves how submissions are reviewed and managed after forms are submitted.

Can editors preview submissions on the original page?
Yes. Authorized users can use form hydration to open a submitted form in its original page context.

Does it support encrypted fields?
No. Encrypted fields are intentionally excluded from hydration and display.

February 12, 2026 →