<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blogs</title><link href="http://world.optimizely.com" /><updated>2026-09-01T16:39:41.0000000Z</updated><id>https://world.optimizely.com/blogs/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>New addon puts the optimization GUI for Optimizely Graph back in the CMS</title><link href="https://www.gulla.net/en/blog/new-addon-puts-the-optimization-gui-for-optimizely-graph-back-in-the-cms/" /><id>I wanted the Graph UI for Pinned Results and Synonyms inside the CMS, so I created another addon.</id><updated>2026-09-01T16:39:41.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>From CMS Content to AI-Ready Content: A Practical Optimizely CMS 12 Approach</title><link href="https://world.optimizely.com/blogs/iliyas-shaikh/dates/2026/8/from-cms-content-to-ai-ready-content-a-practical-optimizely-cms-12-architecture/" /><id>&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;How structured content can help Optimizely CMS editors prepare content for search and AI-powered experiences&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 2;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 18.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Introduction&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The way users discover information is changing.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Traditional search engines return a list of pages. Answer engines provide direct answers, while generative AI experiences can combine information from multiple sources to create responses.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;This has led to three commonly used terms: &lt;strong&gt;SEO, AEO and GEO&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 3;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 13.5pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;SEO, AEO and GEO - What&#39;s the Difference?&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;table class=&quot;MsoNormalTable&quot; style=&quot;mso-cellspacing: 1.5pt; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; mso-yfti-tbllook: 1184; mso-border-insideh: .5pt solid windowtext; mso-border-insidev: .5pt solid windowtext;&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;mso-yfti-irow: 0; mso-yfti-firstrow: yes;&quot;&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;SEO&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;AEO&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;GEO&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;mso-yfti-irow: 1;&quot;&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Focus&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Search engines&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Answer engines&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Generative AI&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 2;&quot;&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Goal&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Help users discover your content&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Help systems provide direct answers&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Help AI understand and use your content&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 3;&quot;&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Editor focus&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Useful, relevant content&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Clear questions and answers&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Clear structured content&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;mso-yfti-irow: 4; mso-yfti-lastrow: yes;&quot;&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Developer focus&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Technical SEO and metadata&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Semantic and structured content&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: .75pt .75pt .75pt .75pt;&quot;&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured content and machine-readable data&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The boundaries between these approaches aren&#39;t strict. Many of the same practices support all three.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;For an &lt;strong&gt;Optimizely CMS developer&lt;/strong&gt;, this leads to an important question:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;How can we make content easier for machines to understand without making content editors responsible for technical implementation?&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Start With the CMS Content Model&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Consider a typical article in Optimizely CMS 12.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;An editor may manage:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Heading&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Introduction&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Author&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Published date&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Modified date&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Featured image&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Main content&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l3 level1 lfo1; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;FAQs&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;These aren&#39;t just pieces of text. They have meaning.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;An &lt;strong&gt;author&lt;/strong&gt; is different from an article heading. A &lt;strong&gt;publication date&lt;/strong&gt; has a different meaning from the main content. An &lt;strong&gt;FAQ question&lt;/strong&gt; has a relationship with its answer.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;This is where structured content becomes important.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Instead of asking the application to interpret everything from rich text, we can model important information explicitly through &lt;strong&gt;Optimizely Content Types and Block Types&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;What Should the Editor See?&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The editor experience should remain simple.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;For example, an Article Page in Optimizely Edit Mode could expose:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Article&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Heading&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Author&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Published Date&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Modified Date&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Featured Image&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Main Content&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l1 level1 lfo2; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;FAQ Blocks&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The editor simply creates and publishes the content.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;They shouldn&#39;t need to see a large field called &lt;strong&gt;&quot;JSON-LD&quot;&lt;/strong&gt; or understand Schema.org syntax.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The principle is:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Editors manage content. Developers manage its technical representation.&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;The Architecture&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;A simple architecture can connect the Optimizely content model with structured data:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;OPTIMIZELY CMS 12&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Content Types &amp;amp; Blocks&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Structured Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Structured Data Layer&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;┌──────────┼──────────┐&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Article&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;FAQ&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Breadcrumb&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;└──────────┼──────────┘&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;JSON-LD&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;┌──────┴──────┐&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;Search&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;AI&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The important part is the separation between &lt;strong&gt;content management&lt;/strong&gt; and &lt;strong&gt;structured-data generation&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Don&#39;t Make Editors Write JSON-LD&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;It can be tempting to expose a JSON-LD text field in the CMS and let editors control everything.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;For most projects, this creates more problems than it solves.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Editors shouldn&#39;t have to understand:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l0 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;JSON syntax&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l0 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Schema.org types&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l0 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Schema properties&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l0 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Required relationships&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l0 level1 lfo3; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Validation rules&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Instead, give editors control over the &lt;strong&gt;information&lt;/strong&gt;, and let the application determine how that information should be represented.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;For example:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Editor&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Author = Sanket Mahimkar&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Published Date = 20 Aug 2026&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Featured Image = Article.jpg&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Application&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Article structured data&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;This provides editorial flexibility while maintaining technical governance.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Let Optimizely Automate What It Already Knows&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Not everything needs an editor field.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Consider breadcrumbs.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Optimizely already knows where a page sits in the content tree:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Start Page&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;└── Technology&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;└── Optimizely&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;└── CMS&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;└── Article&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The application can use this hierarchy to generate breadcrumb structured data.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The editor doesn&#39;t need to maintain the same information twice.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;This leads to another useful principle:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;If the CMS already knows the information, don&#39;t ask the editor to enter it again.&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;One Source of Truth&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;A structured-content approach also reduces duplication.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Without it, we may end up maintaining:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;CMS Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;SEO Fields&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;JSON-LD&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;These can eventually become inconsistent.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Instead:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;CMS Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;│&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;┌─────────┼─────────┐&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;▼&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;▼&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;HTML&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;SEO&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;JSON-LD&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The same underlying content can support multiple experiences.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;If an editor updates the author, date or image, the generated representations can use the updated CMS content.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;h1&gt;What Changes with Optimizely CMS 13?&lt;/h1&gt;
&lt;p&gt;If you&#39;re working with &lt;strong&gt;Optimizely CMS 13&lt;/strong&gt;, this approach becomes even more relevant.&lt;/p&gt;
&lt;p&gt;CMS 13 introduces built-in &lt;strong&gt;Generative Engine Optimization (GEO)&lt;/strong&gt; capabilities. Optimizely&#39;s current CMS 13 tooling includes AI-powered GEO analysis for evaluating content discoverability and can apply JSON-LD schema markup to content.&lt;/p&gt;
&lt;p&gt;This means the architecture we&#39;ve discussed is moving from something developers may need to build themselves toward capabilities that are increasingly part of the Optimizely platform.&lt;/p&gt;
&lt;p&gt;The important distinction is that &lt;strong&gt;structured content is still the foundation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Think of the evolution as:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/d9520f5ff6e34c68a61813a423fec4c1.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;CMS 13 also introduces a more modern content and delivery architecture around&amp;nbsp;&lt;strong&gt;Optimizely Graph&lt;/strong&gt;, with Visual Builder and a unified editing experience.&lt;/p&gt;
&lt;p&gt;So, if you&#39;re starting a new CMS 13 implementation, the question is no longer simply:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;How do I build JSON-LD generation?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It becomes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;How should I model and govern my content so Optimizely&#39;s built-in GEO capabilities can make the best use of it?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is an important shift for developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CMS 12 gives us the architectural opportunity. CMS 13 takes many of those capabilities further into the platform.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Where Does GEO Fit?&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured data is &lt;strong&gt;not GEO by itself&lt;/strong&gt;, and adding JSON-LD doesn&#39;t guarantee that an AI system will cite or recommend a website.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Instead, think of AI readiness as a combination of:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Clear Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured CMS Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Semantic HTML&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Good Information Architecture&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Accurate Metadata&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;+&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured Data&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;For Optimizely developers, the opportunity is to make sure the CMS preserves not just the &lt;strong&gt;text&lt;/strong&gt;, but also the &lt;strong&gt;meaning and relationships behind the content&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;An article has an author.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;An article has a publication date.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;An FAQ has a question and an answer.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;A page has a place in the site hierarchy.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;When this information is structured in the CMS, the application has a much better foundation for creating machine-readable experiences.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Start Small&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;You don&#39;t need to redesign an entire Optimizely CMS 12 implementation.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Start with one content type, such as an Article Page.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Then gradually introduce structured representations for:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Articles&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;FAQs&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Breadcrumbs&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Products&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Events&lt;/span&gt;&lt;/li&gt;
&lt;li class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-list: l2 level1 lfo4; tab-stops: list 36.0pt;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Organizations&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The key is to start with content that provides clear business value.&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; text-align: center; line-height: normal;&quot; align=&quot;center&quot;&gt;&lt;hr /&gt;&lt;/div&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal; mso-outline-level: 1;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 24.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-font-kerning: 18.0pt; mso-fareast-language: EN-IN;&quot;&gt;Conclusion&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Making an Optimizely CMS 12 website ready for an increasingly AI-driven web doesn&#39;t start with JSON-LD.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;It starts with &lt;strong&gt;how we model content&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;When content is structured properly in Optimizely, developers can create reusable mechanisms to expose that information to different consumers, while editors continue working with the familiar CMS experience.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The architecture is simple:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Optimizely Editor&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured Content&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Structured Data&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;JSON-LD&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;&lt;span style=&quot;mso-spacerun: yes;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&amp;darr;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;margin-bottom: .0001pt; line-height: normal; tab-stops: 45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;&quot;&gt;&lt;span style=&quot;font-size: 10.0pt; font-family: &#39;Courier New&#39;; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Search + AI Experiences&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;The principle is even simpler:&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Let editors manage the meaning of the content. Let developers manage how that meaning is represented technically.&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;Your CMS shouldn&#39;t just store content.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot; style=&quot;mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; line-height: normal;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-size: 12.0pt; font-family: &#39;Times New Roman&#39;,serif; mso-fareast-font-family: &#39;Times New Roman&#39;; mso-fareast-language: EN-IN;&quot;&gt;It should preserve what that content means.&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&amp;nbsp;&lt;/p&gt;</id><updated>2026-09-01T11:09:45.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Serving Heavy Multilingual Content Efficiently with Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/8/serving-heavy-multilingual-content-efficiently-with-optimizely-saas-cms/" /><id>&lt;p&gt;When building enterprise-grade applications on modern headless stacks, performance is governed by payload efficiency. Optimizely SaaS CMS and Optimizely Graph provide a fast, flexible foundation for omnichannel digital experiences. As projects grow and complex content requirements emerge &amp;mdash; such as multi-locale video transcripts, technical specifications, or localized legal disclosures &amp;mdash; architects face a challenge that applies to any headless platform and any frontend channel:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we store rich, multi-locale data within a content item without bloating delivery payloads or inflating content item counts?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The good news is that Optimizely SaaS CMS provides native architectural levers &amp;mdash; granular Graph indexing modes, inline component lists, and SDK query synthesis &amp;mdash; that allow teams to solve this challenge elegantly across any channel. This article walks through how to configure these capabilities into a high-performance, cost-efficient omnichannel architecture.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What This Article Covers&lt;/h2&gt;
&lt;p&gt;This guide is for solution architects and senior developers building enterprise digital experiences with Optimizely SaaS CMS, Optimizely Graph, and the Optimizely Content JS SDK. The patterns apply equally to any frontend channel &amp;mdash; Next.js, mobile apps, Vue/Nuxt, SvelteKit, Astro, or embedded displays.&lt;/p&gt;
&lt;p&gt;It connects key platform capabilities into a unified architectural pattern:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Platform Capability&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Architectural Purpose&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Inline Component List Properties&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Attach structured multi-locale data to a page without creating standalone items that count toward subscription quotas&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Granular Graph Indexing Modes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Use Default vs. Queryable indexing to protect schema quotas and avoid silent data loss&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Content JS SDK Query Synthesis&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Control which properties are fetched on initial page render by managing registered TypeScript models&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Two-Way GraphQL Operations&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Target specific language entries on demand and enable catalog-level discovery filtering&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Structured Editorial UX&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Give content authors clean, form-based input fields grouped logically in dedicated tabs&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;What You Will Achieve&lt;/h2&gt;
&lt;p&gt;A production-ready blueprint for storing large, optional multilingual content inside a single Optimizely CMS page item, serving it lazily per locale on demand across any channel, and managing it reliably in the CMS &amp;mdash; all while maintaining optimal page performance and zero licensing overhead.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Architecture at a Glance&lt;/h2&gt;
&lt;p&gt;The pattern spans four layers, each leveraging a native Optimizely capability:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Storage Layer (SaaS CMS):&lt;/strong&gt; Define a reusable component content type &amp;mdash; a language code and a content body &amp;mdash; attached as a list property to the page. Entries are versioned inline as part of the page item, not as standalone objects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Indexing Layer (Optimizely Graph):&lt;/strong&gt; Set the language code to &lt;strong&gt;Queryable&lt;/strong&gt; (enabling catalog-level filters) and the text body to &lt;strong&gt;Default&lt;/strong&gt; (stored and returned for delivery, bypassing full-text search limits and minimizing schema field usage).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fetching Boundary (Content JS SDK):&lt;/strong&gt; Omit the list property from the frontend model registered with &lt;strong&gt;initContentTypeRegistry&lt;/strong&gt;. The SDK&#39;s dynamic runtime query builder fetches only what is declared in your model, keeping initial payloads light.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Delivery Layer (On-Demand Fetching):&lt;/strong&gt; Retrieve the content asynchronously via a targeted GraphQL query only when the user actively requests it &amp;mdash; on any channel, from any client.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;The Headless Payload Tradeoff&lt;/h2&gt;
&lt;p&gt;In high-performance web and mobile applications, initial payloads should contain only what is immediately visible. A 15-minute video transcript across multiple languages can easily exceed 200KB of raw text.&lt;/p&gt;
&lt;p&gt;Without intentional architecture, three problems emerge:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initial payload bloat:&lt;/strong&gt; Returning all language transcripts on standard content queries bloats the response and slows Time to First Byte regardless of the rendering framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Content item proliferation:&lt;/strong&gt; Creating a standalone shared block per language per video multiplies content items across environments. Optimizely licensing is tied to item counts across all environments in an instance, so this compounds quickly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Translation dependencies:&lt;/strong&gt; Language-specific properties require a translated page version to exist before auxiliary data can be stored &amp;mdash; a hard dependency that rarely matches how transcription pipelines or workflows actually operate.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By configuring Optimizely SaaS CMS correctly, all three challenges are resolved simultaneously.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 1: Model the Component as an Inline List Property&lt;/h2&gt;
&lt;p&gt;First, define a reusable component content type holding two properties: an identifier for the language code and the transcript body.&lt;/p&gt;
&lt;h3&gt;The Component Definition (VideoTranscript)&lt;/h3&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;key&quot;: &quot;VideoTranscript&quot;,
  &quot;baseType&quot;: &quot;_component&quot;,
  &quot;displayName&quot;: &quot;Video Transcript&quot;,
  &quot;properties&quot;: {
    &quot;langCode&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;displayName&quot;: &quot;Language Code&quot;,
      &quot;description&quot;: &quot;IETF locale tag, lowercase &amp;mdash; for example en-us or de-de&quot;,
      &quot;indexingType&quot;: &quot;queryable&quot;
    },
    &quot;content&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;displayName&quot;: &quot;Transcript Text&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the component defined, attach it to your page type as an array of inline components (&lt;strong&gt;&quot;type&quot;: &quot;component&quot;&lt;/strong&gt;). In Optimizely SaaS CMS, the &lt;strong&gt;component &lt;/strong&gt;property type indicates an &lt;strong&gt;inline-only composition&lt;/strong&gt;, ensuring that entries are embedded directly within the parent page&#39;s storage and lifecycle rather than creating standalone referenced entities.&lt;/p&gt;
&lt;h3&gt;The Page Type (VideoPage)&lt;/h3&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;key&quot;: &quot;VideoPage&quot;,
  &quot;baseType&quot;: &quot;_page&quot;,
  &quot;displayName&quot;: &quot;Video Page&quot;,
  &quot;properties&quot;: {
    &quot;title&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;displayName&quot;: &quot;Title&quot;,
      &quot;localized&quot;: true
    },
    &quot;videoUrl&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;displayName&quot;: &quot;Video URL&quot;,
      &quot;localized&quot;: true
    },
    &quot;transcripts&quot;: {
      &quot;type&quot;: &quot;array&quot;,
      &quot;displayName&quot;: &quot;Transcripts&quot;,
      &quot;description&quot;: &quot;One entry per language. Shared across all locale versions of this page.&quot;,
      &quot;localized&quot;: false,
      &quot;items&quot;: {
        &quot;type&quot;: &quot;component&quot;,
        &quot;contentType&quot;: &quot;VideoTranscript&quot;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting &lt;strong&gt;localized: false&lt;/strong&gt; on the list means all language transcripts live on the master locale version of the page. Automated pipelines can populate translations immediately without waiting for localized page versions to exist.&lt;/p&gt;
&lt;h3&gt;Architectural Tradeoff: component vs. content Property Types&lt;/h3&gt;
&lt;p&gt;While array properties in Optimizely SaaS CMS can hold primitive types (strings, numbers, booleans) or complex structures, arrays of structured data typically choose between &lt;strong&gt;component &lt;/strong&gt;and &lt;strong&gt;content &lt;/strong&gt;as their item type:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&quot;type&quot;: &quot;component&quot;&lt;/strong&gt;&lt;strong&gt; (used in this pattern):&lt;/strong&gt; Inline-only. Entries are permanently embedded within the parent page and cannot be extracted or referenced as standalone blocks. This is the safer choice when keeping item counts flat is a priority.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&quot;type&quot;: &quot;content&quot;&lt;/strong&gt;&lt;strong&gt; (alternative):&lt;/strong&gt; Inline or referenced. Provides a future migration path if transcripts ever need to be reused across multiple items &amp;mdash; but editors can also unintentionally author entries as standalone shared blocks, which increases repository item counts and can affect licensing costs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 2: Choose the Right Indexing Type for Each Property&lt;/h2&gt;
&lt;p&gt;Optimizely SaaS CMS gives developers per-property control over how content is indexed in Optimizely Graph:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Indexing Type&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Returned in GraphQL&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Usable in where Filters&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Full-Text Searchable&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Key Consideration&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Default&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Ideal for large display text. Bypasses search term limits and minimizes schema field consumption&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Queryable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Ideal for codes and identifiers. Enables structured filtering without text tokenization&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Searchable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Yes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Enables full-text search. Subject to Elasticsearch&#39;s 8,191-byte per-term limit&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Disabled&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Excluded from the Graph schema entirely&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For this pattern:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;langCode set to Queryable:&lt;/strong&gt; A short string with negligible indexing cost. Being filterable enables queries such as &quot;return every video that has a German transcript&quot; without fetching transcript bodies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;content set to Default:&lt;/strong&gt; The full transcript is stored in Optimizely Graph and returned when queried, but it is excluded from full-text indexing. Strings marked as Searchable that exceed 8,191 bytes are silently dropped from the index with no error at publish time. Default sidesteps this entirely and consumes fewer fields against the instance&#39;s 100,000-field schema quota.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 3: Decouple the Frontend Model from the SDK Query Builder&lt;/h2&gt;
&lt;p&gt;When using automatic routing or calling &lt;strong&gt;getContentByPath()&lt;/strong&gt;, the Optimizely Content JS SDK inspects the models registered in your application and synthesizes a GraphQL query containing every property declared in them. That behavior is the control point: a property absent from the registered model is never requested.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;// src/models/VideoPage.ts

// Registered with the SDK.
// transcripts is deliberately absent so the runtime query builder excludes it.
export interface VideoPage {
  _metadata: ContentMetadata;
  title: string;
  videoUrl: string;
}

// Used only by the on-demand query.
export interface TranscriptEntry {
  langCode: string;
  content: string;
}

export interface VideoPageTranscripts {
  transcripts?: TranscriptEntry[];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register only the core model at application bootstrap:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;// src/app/layout.tsx
import { initContentTypeRegistry } from &#39;@optimizely/cms-sdk&#39;;
import { VideoPage } from &#39;./models/VideoPage&#39;;

initContentTypeRegistry([
  {
    type: &#39;VideoPage&#39;,
    model: VideoPage
  }
]);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Splitting the interfaces makes accidental inclusion of the heavy field a compile error rather than a silent payload regression. The property remains fully queryable through dedicated operations because it exists in the Graph schema.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Step 4: Two-Way Querying via Optimizely Graph&lt;/h2&gt;
&lt;p&gt;Defining &lt;strong&gt;langCode&lt;/strong&gt; as a structured, &lt;strong&gt;Queryable&lt;/strong&gt; property unlocks a powerful two-way querying synergy in Optimizely Graph: targeted on-demand retrieval for a single item, and catalog-level discovery filtering across your entire content repository.&lt;/p&gt;
&lt;h3&gt;Direction 1: Targeted Transcript Retrieval (Item &amp;rarr; Language)&lt;/h3&gt;
&lt;p&gt;When a user on a specific video page clicks &quot;Show Transcript&quot; or selects a subtitle language on any client (web, mobile app, or smart display), your application fires a targeted GraphQL query retrieving only the &lt;strong&gt;transcripts &lt;/strong&gt;property for that specific content ID:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;query GetVideoTranscript($id: String!) {
  VideoPage(where: { _metadata: { key: { eq: $id } } }) {
    items {
      transcripts {
        langCode
        content
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your client (or a lightweight server route / API handler) executes this query against Optimizely Graph, resolves the matching &lt;strong&gt;langCode &lt;/strong&gt;entry in memory, and caches the response. Initial page rendering remains instant because zero transcript bytes were fetched upfront.&lt;/p&gt;
&lt;h3&gt;Direction 2: Catalog Discovery Filtering (Language &amp;rarr; Items)&lt;/h3&gt;
&lt;p&gt;In reverse, you can query across your entire video catalog to discover every video that offers a transcript in a specific language &amp;mdash; without fetching any transcript text:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;query VideosWithGermanTranscripts {
  VideoPage(where: { transcripts: { langCode: { eq: &quot;de-de&quot; } } }) {
    items {
      _metadata { key displayName }
      title
      videoUrl
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This unlocks listing and search capabilities (e.g., &lt;em&gt;&quot;Show only videos with German subtitles&quot;&lt;/em&gt;) while keeping query execution fast and lightweight.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Editorial Experience&lt;/h2&gt;
&lt;p&gt;A structured component list gives content authors form fields &amp;mdash; a language code input and a text area per entry &amp;mdash; rather than a raw text box holding serialized data. Entries can be added, reordered, and removed through the standard list interface in the CMS editor.&lt;/p&gt;
&lt;p&gt;Two configuration refinements are worth applying:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Group the transcripts property into a dedicated tab (Metadata, or similar) in the content type settings, so the primary editing surface stays focused on the content editors work with most.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Standardize language codes as lowercase IETF tags and document the convention in the property&#39;s help text. Your application&#39;s locale fallback logic depends on predictable casing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Omnichannel Applicability&lt;/h2&gt;
&lt;p&gt;This pattern is not specific to any frontend framework. The storage and indexing decisions live entirely within Optimizely SaaS CMS and Optimizely Graph. Any channel consuming content through the Graph API can apply the same approach:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;A mobile app (iOS, Android, React Native, Flutter) fetches core content on screen load and requests transcripts via a separate API call when the user opens the subtitle panel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A Next.js, Nuxt, or SvelteKit application fetches core content during page render and fetches transcripts lazily on user interaction.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A kiosk or digital signage display requests transcripts only when the user activates accessibility mode.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The boundary between what is fetched by default and what is fetched on demand is defined by the component model and the SDK registration &amp;mdash; not by the channel.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What This Pattern Delivers&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Dimension&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Fetching everything by default&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;&lt;strong&gt;Selective loading with a component list&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Initial payload&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Includes all language content on every render&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;No transcript bytes during initial load&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Page and screen performance&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Slower TTFB and delayed first render&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Payload proportional to visible content&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Content item count&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Multiplies per language when using shared blocks&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Unchanged &amp;mdash; entries are stored inline&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Graph field usage&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Higher when properties are searchable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;One Queryable field, one Default field&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Byte-limit exposure&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Silent index drops above 8,191 bytes&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Avoided by using the Default indexing type&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Translation dependency&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Blocked until the page is translated&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Decoupled &amp;mdash; entries live on the master locale&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Data integrity&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Errors surface at render time&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Validated at the API boundary on write&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The underlying principle applies well beyond transcripts. Any property that is large, optional, and consumed by a subset of users &amp;mdash; specification sheets, legal text, changelogs, structured FAQ data &amp;mdash; benefits from the same separation between what the CMS stores and what any given channel requests by default.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;The code samples in this article illustrate the pattern rather than serving as a drop-in implementation.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-08-31T10:22:16.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>The Under-Documented Optimizely Caching API I Somehow Missed</title><link href="https://reillydom.com/blog/optimizely-readthrough-master-keys" /><id>I recently discovered some useful, but poorly documented, caching features in Optimizely CMS. Here’s how ReadThrough, ReadStrategy.Wait and master keys can prevent cache stampedes and invalidate custom cached data when Optimizely content changes.</id><updated>2026-08-31T09:18:39.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Beyond [Authorize]: Function-Level Permissions in Optimizely</title><link href="https://world.optimizely.com/blogs/sanjay-katiyar/dates/2026/8/beyond-authorize-function-level-permissions-in-optimizely/" /><id>&lt;p&gt;Why basic role checks fall short and how Permissions to Functions unlock audience-based access control&lt;/p&gt;
&lt;h3&gt;The Problem with Basic Authorization&lt;/h3&gt;
&lt;p&gt;ASP.NET Core&#39;s [Authorize] attribute is the right starting point for access control. It answers a simple question: is this user allowed into the application at all?&lt;/p&gt;
&lt;p&gt;For many endpoints, that is enough. But as business rules grow more specific, [Authorize] starts to show its limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only checks whether the user is authenticated or belongs to a role or policy&lt;/li&gt;
&lt;li&gt;It does not easily support dynamic business rules&lt;/li&gt;
&lt;li&gt;It is not ideal for audience-based or context-based permissions&lt;/li&gt;
&lt;li&gt;It becomes hard to manage when access depends on function-level rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;A familiar scenario&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A user may be fully authenticated and even hold a valid CMS role yet still should not be allowed to perform a specific action.&lt;/p&gt;
&lt;p&gt;Some features need access for a specific audience, not just a role name baked into code. That gap is where many Optimizely implementations start looking for a better pattern.&lt;/p&gt;
&lt;h3&gt;The Limitation of Built-In Permission Checks Alone&lt;/h3&gt;
&lt;p&gt;Optimizely exposes &lt;span style=&quot;color: rgb(0, 0, 238);&quot;&gt;&lt;a style=&quot;color: rgb(0, 0, 238);&quot; href=&quot;https://docs.developers.optimizely.com/content-management-system/docs/permissions-to-functions&quot;&gt;Permissions to Functions&lt;/a&gt; &lt;/span&gt;through PermissionType and PermissionService. That is a strong foundation: permissions are discoverable in the admin UI and can be assigned to users and roles.&lt;/p&gt;
&lt;p&gt;However, relying only on the built-in authorization path has a gap of its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;color: rgb(45, 194, 107);&quot;&gt;&lt;span style=&quot;color: rgb(45, 194, 107);&quot;&gt;Role and group assignment works well&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: rgb(186, 55, 42);&quot;&gt;&lt;span style=&quot;color: rgb(186, 55, 42);&quot;&gt;Visitor group / audience-based evaluation is not wired into a simple declarative attribute out of the box&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;color: rgb(230, 126, 35);&quot;&gt;&lt;span style=&quot;color: rgb(230, 126, 35);&quot;&gt;Controllers end up with duplicated authorization logic, or hard-coded role lists that drift from CMS configuration&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is often a mix of [Authorize(Roles = &quot;...&quot;)] strings scattered across controllers, difficult to audit and painful to change when the business audience evolves.&lt;/p&gt;
&lt;h2&gt;The Solution: AuthorizePermission with Permissions to Functions&lt;/h2&gt;
&lt;p&gt;Instead of relying only on [Authorize], use a custom authorization attribute backed by Optimizely&#39;s Permissions to Functions model.&lt;/p&gt;
&lt;p&gt;This approach lets you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Control access at the function level&lt;/li&gt;
&lt;li&gt;Evaluate business rules dynamically&lt;/li&gt;
&lt;li&gt;Support audience-based permission checks (users, roles, and visitor groups)&lt;/li&gt;
&lt;li&gt;Keep authorization logic reusable and centralized&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Key difference at a glance&lt;/strong&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 65.4292%; border-spacing: 5px; border: 1px solid rgb(126, 140, 141); height: 55.7499px;&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16.5833px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 16.5833px;&quot;&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 16.5833px;&quot;&gt;&lt;strong&gt;What it checks&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 16.5833px;&quot;&gt;&lt;strong&gt;&amp;nbsp;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;[Authorize]&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;Authentication, roles, policies&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;App-level and coarse-grained access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;&lt;strong&gt;Permissions to Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;CMS-managed audience per permission&lt;/td&gt;
&lt;td style=&quot;border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141); height: 19.5833px;&quot;&gt;Function-level, business-aligned access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;[Authorize] = basic, framework-level access control&lt;br /&gt;CustomAuthorizePermission = custom, function-level permission control&lt;/p&gt;
&lt;h2&gt;How It Works in Practice&lt;/h2&gt;
&lt;p&gt;The pattern has three parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define permission types - discovered by Optimizely and shown under Config &amp;rarr; Permissions to functions&lt;/li&gt;
&lt;li&gt;Register virtual roles (optional) - map permissions to virtual roles for broader CMS integration&lt;/li&gt;
&lt;li&gt;Apply a custom filter attribute - evaluate the current user against the permission audience at request time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Define permission types&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create a static class decorated with [PermissionTypes]. Each PermissionType represents a function your application exposes, for example, viewing reports, managing settings, or accessing an admin area.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;[PermissionTypes]
public static class AppPermissions
{
    public const string GroupName = &quot;MyApplication&quot;;

    static AppPermissions()
    {
        ViewReports = new PermissionType(GroupName, nameof(ViewReports));
        ManageSettings = new PermissionType(GroupName, nameof(ManageSettings));
        AccessAdmin = new PermissionType(GroupName, nameof(AccessAdmin));
    }

    public static PermissionType ViewReports { get; private set; }
    public static PermissionType ManageSettings { get; private set; }
    public static PermissionType AccessAdmin { get; private set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once deployed, these permissions appear in the Optimizely admin UI (CMS --&amp;gt; Settings --&amp;gt; Permissions for Functions). Admins can assign users and roles to each function without redeploying code.&lt;br /&gt;&lt;br /&gt;Add localization so friendly names and descriptions appear in&amp;nbsp; Permissions for functions instead of raw type names. Create or update a language XML file (for example Resources/Localization/Views_en.xml):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; standalone=&quot;yes&quot;?&amp;gt;
&amp;lt;languages&amp;gt;
  &amp;lt;language name=&quot;English&quot; id=&quot;en&quot;&amp;gt;
    &amp;lt;admin&amp;gt;
      &amp;lt;permissiontype&amp;gt;
        &amp;lt;groups&amp;gt;
          &amp;lt;AppPermissions&amp;gt;
            &amp;lt;description&amp;gt;Functions in the application&amp;lt;/description&amp;gt;
            &amp;lt;permissions&amp;gt;
              &amp;lt;ViewReports&amp;gt;Allows users to view reports&amp;lt;/ViewReports&amp;gt;
              &amp;lt;ManageSettings&amp;gt;Allows users to manage application settings&amp;lt;/ManageSettings&amp;gt;
              &amp;lt;AccessAdmin&amp;gt;Allows users to access the admin area&amp;lt;/AccessAdmin&amp;gt;
            &amp;lt;/permissions&amp;gt;
          &amp;lt;/AppPermissions&amp;gt;
        &amp;lt;/groups&amp;gt;
      &amp;lt;/permissiontype&amp;gt;
    &amp;lt;/admin&amp;gt;
  &amp;lt;/language&amp;gt;
&amp;lt;/languages&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Register virtual roles (optional)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you need permissions to participate in Optimizely&#39;s role system, for example, in content access rules or visitor group criteria, register a virtual role for each permission:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class AppVirtualRoleInitialization : IInitializableModule
{
&amp;nbsp; &amp;nbsp; public void Initialize(InitializationEngine context)
&amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var virtualRoleRepository = context.Locate.Advanced
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .GetInstance();

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; virtualRoleRepository.Register(&quot;ViewReportsVirtualRole&quot;, new PermissionRole
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Permission = AppPermissions.ViewReports
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; virtualRoleRepository.Register(&quot;ManageSettingsVirtualRole&quot;, new PermissionRole
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Permission = AppPermissions.ManageSettings
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Register additional permissions as needed
&amp;nbsp; &amp;nbsp; }

&amp;nbsp; &amp;nbsp; public void Uninitialize(InitializationEngine context) { }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This step is optional. Skip it if you only need controller-level authorization via a custom attribute.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Apply a custom authorization attribute&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create an IAsyncAuthorizationFilter that resolves the permission audience at request time. A typical implementation supports two evaluation paths:&lt;/p&gt;
&lt;p&gt;By PermissionType - calls PermissionService.IsPermitted for direct function-level checks against users and roles assigned in the CMS&lt;/p&gt;
&lt;p&gt;By PermissionName - resolves visitor groups assigned to the permission via PermissionRepository and evaluates them with IVisitorGroupRole.IsMatch&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;
namespace MyApplication.Attributes
{
    using System;
    using System.Linq;
    using System.Threading.Tasks;

    using EPiServer.DataAbstraction;
    using EPiServer.Find.Helpers.Text;
    using EPiServer.Personalization.VisitorGroups;
    using EPiServer.Security;

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.DependencyInjection;

    using MyApplication.Infrastructure.Security;

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
    public class AuthorizePermissionAttribute : ActionFilterAttribute, IAsyncAuthorizationFilter
    {
        public string PermissionName { get; set; }

        public PermissionType? PermissionType { get; set; }

        public AuthorizePermissionAttribute()
        {
        }

        public AuthorizePermissionAttribute(string name)
        {
            this.PermissionName = name;
        }

        public AuthorizePermissionAttribute(string groupName, string name)
        {
            this.PermissionType = new PermissionType(groupName, name);
        }

        public async Task OnAuthorizationAsync(AuthorizationFilterContext filterContext)
        {
            if (this.PermissionType == null &amp;amp;&amp;amp; this.PermissionName.IsNullOrEmpty())
            {
                return;
            }

            if (this.PermissionName.IsNotNullOrEmpty())
            {
                await this.AuthorizeByPermissionNameAsync(filterContext);
                return;
            }

            this.AuthorizeByPermissionType(filterContext);
        }

        private async Task AuthorizeByPermissionNameAsync(AuthorizationFilterContext filterContext)
        {
            var httpContext = filterContext.HttpContext;
            var user = httpContext.User;

            var permissionRepository = httpContext.RequestServices.GetRequiredService();
            var visitorGroupRoleRepository = httpContext.RequestServices.GetRequiredService();

            var permissionType = GetPermissionType(this.PermissionName);
            var audiences = await permissionRepository.GetPermissionsAsync(permissionType);

            var visitorGroupNames = audiences
                .Where(x =&amp;gt; x.EntityType == SecurityEntityType.VisitorGroup)
                .Select(x =&amp;gt; x.Name)
                .Distinct(StringComparer.OrdinalIgnoreCase)
                .ToList();

            foreach (var visitorGroupName in visitorGroupNames)
            {
                var hasRole = visitorGroupRoleRepository.TryGetRole(visitorGroupName, out var visitorGroupRole);
                if (!hasRole)
                {
                    continue;
                }

                var isMatch = visitorGroupRole.IsMatch(user, httpContext);
                if (isMatch)
                {
                    return;
                }
            }

            filterContext.Result = new ChallengeResult();
        }

        private void AuthorizeByPermissionType(AuthorizationFilterContext filterContext)
        {
            var httpContext = filterContext.HttpContext;
            var user = httpContext.User;

            var permissionService = httpContext.RequestServices.GetRequiredService();
            var isPermitted = permissionService.IsPermitted(user, this.PermissionType);

            if (isPermitted)
            {
                return;
            }

            var isAuthenticated = user.Identity != null &amp;amp;&amp;amp; user.Identity.IsAuthenticated;
            filterContext.Result = GetUnauthorizedResult(isAuthenticated);
        }

        private static IActionResult GetUnauthorizedResult(bool isAuthenticated)
        {
            if (!isAuthenticated)
            {
                return new ChallengeResult();
            }

            return new ForbidResult();
        }

        private static PermissionType? GetPermissionType(string permissionName)
        {
            switch (permissionName)
            {
                case nameof(AppPermissions.ViewReports):
                    return AppPermissions.ViewReports;
                case nameof(AppPermissions.ManageSettings):
                    return AppPermissions.ManageSettings;
                case nameof(AppPermissions.AccessAdmin):
                    return AppPermissions.AccessAdmin;
                default:
                    return null;
            }
        }
    }
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply the attribute on any controller or action that should be gated by a CMS-managed audience:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;
// By permission name evaluates visitor groups assigned to the permission
[AuthorizePermission(nameof(AppPermissions.ViewReports))]
public class ReportsController : Controller
{
    // Only users matching the ViewReports visitor group audience can access this
}

// By permission type evaluates users and roles via PermissionService
[AuthorizePermission(&quot;AppPermissions&quot;, nameof(AppPermissions.ManageSettings))]
public class SettingsController : Controller
{
    // Only users in the ManageSettings permission audience can access this
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The permission group name and function name must match what you defined in Step 1. The audience users, roles, and optionally visitor groups is managed entirely in the Optimizely admin UI.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;nbsp;When to Use Each Approach&lt;/strong&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 59.4105%; height: 117.5px; border-spacing: 5px; border: 1px solid rgb(126, 140, 141);&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;height: 19.5833px; width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19.5833px; width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;&lt;strong&gt;Recommended approach&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;height: 19.5833px; width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;Public or anonymous endpoints&lt;/td&gt;
&lt;td style=&quot;width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;[Authorize]&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;height: 19.5833px; width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;Any authenticated CMS user&lt;/td&gt;
&lt;td style=&quot;width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;[Authorize(Roles = &quot;...&quot;)]&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;Fixed role-based access with no CMS audience&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;AuthorizePermission + PermissionTypes&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;Function-level access managed in Optimizely admin&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;AuthorizePermission with permission name resolution&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19.5833px;&quot;&gt;
&lt;td style=&quot;width: 47.6037%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;Audience includes visitor groups&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 52.3118%; border-width: 1px; padding: 5px; border-color: rgb(126, 140, 141);&quot;&gt;
&lt;p&gt;Custom attribute + hybrid auth filter&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;More flexible than [Authorize]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Roles and policies are static contracts. Permissions to Functions are business contracts managed in the CMS and mapped to real audiences.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Easier to scale&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As new features ship, add a PermissionType and an attribute. No more hunting through controllers for magic role strings.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Better alignment with business needs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Marketing, operations, and support teams can reason about who can access Orders or who can change organization context through the admin UI not through source code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cleaner separation of concerns&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Authentication establishes identity&lt;/li&gt;
&lt;li&gt;Authorization evaluates function-level audience membership&lt;/li&gt;
&lt;li&gt;Controllers stay focused on business logic&lt;/li&gt;
&lt;/ul&gt;</id><updated>2026-08-28T16:32:40.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Insights from upgrading Optimizely CMS 12 to CMS 13</title><link href="http://parwissmark.wordpress.com/?p=447" /><id>Optimizely CMS 13 has been here a while now, so I wanted to share some insights from a few of the migrations that has been done. The framework and package upgrade itself was fairly straightforward, but there were a number of API changes, older CMS 12 patterns and a few runtime issues that only showed [&amp;#8230;]</id><updated>2026-08-28T11:29:38.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Copies vs. Connections: Modeling Content Areas for Multilingual Success</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/8/copies-vs.-connections-modeling-content-areas-for-multilingual-success" /><id>&lt;h2&gt;Starting With the Simple Question&lt;/h2&gt;
&lt;p&gt;Most multilingual content modeling conversations start with a deceptively simple question from a client:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;If we set this image once in English, will it show up automatically in French?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It sounds like a yes-or-no question. In Optimizely SaaS CMS, the honest answer is: &lt;strong&gt;it depends entirely on how that image was modeled.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before diving into anything complex, here are three quick sanity checks worth running on your own project. If you can answer all three confidently, you are probably in good shape:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open a translated page in your CMS.&lt;/strong&gt; Change something in the master language. Does the translated version follow, or stay as it was?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Look at a Content Area in the editor.&lt;/strong&gt; Can you tell, at a glance, what each item inside it actually is?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ask a translator to open a page.&lt;/strong&gt; Do they immediately know which field maps to which part of the design?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If any of those made you pause, this post is written for you. Nothing here is exotic or advanced. It is simply about understanding what the platform stores where, and choosing shapes that match what your editors and translators actually need.&lt;/p&gt;
&lt;p&gt;This is an area where reasonable teams land on different answers, so treat what follows as a starting point for your own discussion rather than a fixed rule set.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Designing for Both the Code and the Editor&lt;/h2&gt;
&lt;p&gt;One of the genuine strengths of Optimizely SaaS CMS is its code-first, headless approach. Developers define content types directly in TypeScript and push them straight to the CMS, keeping the model in version control alongside the front-end application.&lt;/p&gt;
&lt;p&gt;But a content model is not only a database schema or a GraphQL query shape. It is also the daily interface for your editorial team.&lt;/p&gt;
&lt;p&gt;When we model content, we are designing the workspace that authors, content managers, and translators will live in. Model purely for developer convenience and it is easy to hand editors something that is awkward to navigate, fragile across languages, and difficult to translate cleanly.&lt;/p&gt;
&lt;p&gt;This post focuses on one specific area of that decision space: &lt;strong&gt;Content Areas&lt;/strong&gt;, and how the items inside them behave once you introduce a second language.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Core Building Blocks&lt;/h2&gt;
&lt;p&gt;There are three primary ways to structure content within a page or experience in Optimizely SaaS CMS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flat properties&lt;/strong&gt; are scalar fields defined directly on the content type, such as a &lt;strong&gt;string&lt;/strong&gt;, a &lt;strong&gt;contentReference&lt;/strong&gt;, or a &lt;strong&gt;boolean&lt;/strong&gt;. They are simple, predictable, and the easiest to reason about across languages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Embedded components&lt;/strong&gt; are nested data structures stored inside the parent page&#39;s data as a cohesive group. Their lifecycle is bound to the parent, so publishing the page publishes the component with it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Content arrays&lt;/strong&gt; are the headless equivalent of a &lt;strong&gt;Content Area&lt;/strong&gt;. They hold a collection of content items, and those items can be one of two kinds:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Shared blocks&lt;/strong&gt; are reusable items that live in the Assets panel with their own publishing workflow and their own locale branches.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inline blocks&lt;/strong&gt; are created on the fly inside the Content Area. They have no independent identity and their lifecycle is tied entirely to the parent page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is a good fit for something. The interesting question is which one fits the requirement in front of you.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The SHARED Label: A Signal Worth Teaching&lt;/h2&gt;
&lt;p&gt;One of the most useful affordances in the CMS editing interface is a small label: &lt;strong&gt;SHARED&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When an author opens a Content Area, each item inside it is visually marked:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Shared blocks&lt;/strong&gt; show a &lt;strong&gt;SHARED&lt;/strong&gt; badge next to their name.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inline blocks&lt;/strong&gt; show no badge at all.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not decoration. It is a live signal about editorial reach:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you see SHARED, this block is a connected item. Editing it affects every page that references it, and for non-localizable properties it updates all language versions at once.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An inline block, by contrast, is a private copy owned by that one page. On screen the two look almost identical. The consequences of editing them are quite different.&lt;/p&gt;
&lt;p&gt;This is worth five minutes in editor onboarding. It is one of the simplest ways to prevent a well-meaning edit from rippling further than anyone intended.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Naming Habits: Small Effort, Large Payoff&lt;/h2&gt;
&lt;p&gt;When an author creates an inline block inside a Content Area, the CMS offers a Name field. It is optional. Leave it blank and the CMS fills it with the content type name.&lt;/p&gt;
&lt;p&gt;The code compiles and the API returns everything correctly. But let&#39;s look at what actually happens in the editor&#39;s workspace on a multilingual site when we don&#39;t establish a naming standard.&lt;/p&gt;
&lt;p&gt;Imagine an editor builds a Modular Landing Page using a Content Area, where they can add a flexible sequence of blocks: a hero banner, three promotional cards, and a call-to-action button. Because a landing page is highly dynamic, a flexible Content Area is exactly the right tool.&lt;/p&gt;
&lt;p&gt;However, if they leave the optional Name fields blank during block creation, the CMS defaults each name to the block&#39;s content type (such as &lt;strong&gt;PromoCard&lt;/strong&gt; or &lt;strong&gt;HeroBanner&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;Here is what the English and French editors see side-by-side in their page editor:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;THE UNNAMED PAIN POINT (What the translator sees)

English Page                          French Page (Copied)
Landing Page                          Landing Page
 |- HeroBanner                         |- HeroBanner
 |- PromoCard                          |- PromoCard
 |- PromoCard                          |- PromoCard
 |- PromoCard                          |- PromoCard
 |- CTABlock                           |- CTABlock&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt; The list is completely anonymous. If the French translator wants to translate the text for the &quot;Free Shipping&quot; card vs. the &quot;20% Discount&quot; card, they have no idea which &lt;strong&gt;PromoCard&lt;/strong&gt; is which. They have to click and open every single inline block on both sides of the screen to match them up. This turns a straightforward translation task into a tedious guessing game.&lt;/p&gt;
&lt;h3&gt;A Convention That Solves It&lt;/h3&gt;
&lt;p&gt;Now look at the difference when we introduce a simple, structured naming convention. If we instruct authors to prefix their inline blocks with a simple numeric order and a functional marketing label, the translation workspace transforms:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;THE NAMED BEST PRACTICE (Effortless translation)

English Page                          French Page (Copied)
Landing Page                          Landing Page
 |- 1. Hero - Summer Promo             |- 1. Hero - Summer Promo
 |- 2. Card - Free Shipping            |- 2. Card - Free Shipping
 |- 3. Card - 20% Discount             |- 3. Card - 20% Discount
 |- 4. Card - New Arrivals             |- 4. Card - New Arrivals
 |- 5. CTA - Sign Up                   |- 5. CTA - Sign Up&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The Benefit:&lt;/strong&gt; Because the names are cloned during the copy-to-language flow, the French translator immediately knows the context of each block. They click exactly the card they want, translate the English text to French, and can optionally update the block names if they choose. The page layout remains perfectly clear, organized, and predictable.&lt;/p&gt;
&lt;p&gt;For shared blocks, a meaningful name is even more critical because that name appears in the global Assets panel where every editor on the project finds, filters, and manages them:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Assets panel
 |- Global Blocks
     |- Homepage Hero Banner (SHARED)
     |- Q3 Promo CTA (SHARED)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Agreeing this convention before the first content entry session is a small investment that keeps paying off as the site grows.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Multilingual Flow: Copies and Connections&lt;/h2&gt;
&lt;p&gt;Optimizely SaaS CMS makes creating a language variant straightforward. When you add a French version of an English page, the CMS offers to &lt;strong&gt;copy content from English&lt;/strong&gt;, duplicating the structure and cloning the inline block data so translators start from a complete page rather than a blank one.&lt;/p&gt;
&lt;p&gt;What happens after that copy depends on whether the items are inline or shared.&lt;/p&gt;
&lt;h3&gt;Inline Blocks: A Snapshot in Time&lt;/h3&gt;
&lt;p&gt;Inline blocks have no independent key, so they belong entirely to the language version of the page that owns them. Once the copy is made, the two sets of blocks are separate. Update the image in the English block and the French page keeps the one it already had. Fix a typo in English and the French text stays as it was. Add or reorder blocks in English and the French arrangement is unaffected.&lt;/p&gt;
&lt;p&gt;This is reasonable behaviour, and for some teams it is exactly what they want. It gives regional editors complete freedom to shape their own market&#39;s content without a central team overwriting their work. The trade-off is that any master-language change needs to be applied again in each translated version.&lt;/p&gt;
&lt;p&gt;Where this catches teams out is when the expectation was the opposite. If a client believes they are setting a value once for all markets, this is the moment to talk it through.&lt;/p&gt;
&lt;h3&gt;Shared Blocks: A Genuine Connection&lt;/h3&gt;
&lt;p&gt;Shared blocks are content items in their own right, with their own key and their own locale branches. When both the English and French pages reference the same shared block, they are pointing at the &lt;strong&gt;same item&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For properties marked as non-localizable, the value is held once and served to every language version of that block. Change it in the master language and all languages reflect it. In the editor, those fields appear as read-only when working in a non-master language, with an indicator showing where the value comes from.&lt;/p&gt;
&lt;p&gt;For properties marked as localizable, each locale branch keeps its own value, exactly as you would expect for translatable text.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Localizable Setting&lt;/h2&gt;
&lt;p&gt;The SDK exposes a &lt;strong&gt;localizable&lt;/strong&gt; flag on property definitions. Setting it to false means one shared value across languages; setting it to true means a unique value per language version.&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;export const ImageWithAltCT = contentType({
  key: &#39;ImageWithAlt&#39;,
  baseType: &#39;_component&#39;,
  properties: {
    image: {
      type: &#39;contentReference&#39;,
      allowedTypes: [&#39;_image&#39;],
      displayName: &#39;Image&#39;,
      localizable: false,
    },
    altText: {
      type: &#39;string&#39;,
      displayName: &#39;Alt text&#39;,
      localizable: true,
    },
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CLI maps this to the &lt;strong&gt;Unique value per language&lt;/strong&gt; setting on the property when types are pushed to the CMS.&lt;/p&gt;
&lt;h3&gt;Where the Setting Creates a Connection&lt;/h3&gt;
&lt;p&gt;This is the part most worth internalising, because the same setting behaves differently depending on where the property lives.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0); padding: 10px; width: 20.8323%;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Where the property lives&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; background-color: rgb(236, 240, 241); border: 1px solid rgb(0, 0, 0); padding: 10px; width: 79.1677%;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Effect of setting localizable to false&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 20.8323%;&quot;&gt;
&lt;p&gt;Flat property on a page&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 79.1677%;&quot;&gt;
&lt;p&gt;One value shared by every language version of that page. Change it in the master language and all locales reflect it.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 20.8323%;&quot;&gt;
&lt;p&gt;Property on a shared block&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 79.1677%;&quot;&gt;
&lt;p&gt;One value shared across all locale branches of that block. Both the English and French pages referencing it see the update.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 20.8323%;&quot;&gt;
&lt;p&gt;Property inside an inline block&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px; width: 79.1677%;&quot;&gt;
&lt;p&gt;The setting still governs whether the field is translatable, but there are two separate anonymous copies. A change in the English copy does not reach the French copy.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The underlying principle is straightforward once you see it: &lt;strong&gt;a shared value needs something stable to attach to.&lt;/strong&gt; Flat properties and shared blocks both have a stable identity. Inline blocks do not, so there is nothing for the two language versions to share.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Pairing Shared Blocks With Localizable Settings&lt;/h2&gt;
&lt;p&gt;When you need grouped content, such as an image with its alt text, and that group needs to behave sensibly across languages, combining a shared block with selective localizable settings is a clean approach worth considering.&lt;/p&gt;
&lt;p&gt;Using the definition above as a shared block, referenced from both the English and French pages, gives you this behaviour:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Result&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;English editor selects a different image&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;French page shows the new image, since the property is non-localizable on a shared item&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;French translator updates the French alt text&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;English alt text is unaffected&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;English editor updates the English alt text&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;French alt text is unaffected&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The result is the split many multilingual projects are reaching for: shared structural assets, independently translated text, enforced by the platform rather than by editorial vigilance.&lt;/p&gt;
&lt;p&gt;Worth noting honestly: this pattern also means the shared block is genuinely shared. Editing it affects every page referencing it. That is the intent, but it should be a deliberate choice rather than a side effect, and the SHARED badge is what makes it visible to authors.&lt;/p&gt;
&lt;h3&gt;Choosing Between Flat Properties and Shared Blocks&lt;/h3&gt;
&lt;p&gt;Both approaches keep languages aligned. The question that usually separates them is whether the content is genuinely reused.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Approach worth considering&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Image and alt text in one fixed slot on one page&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Flat &lt;strong&gt;contentReference&lt;/strong&gt; set to non-localizable, plus a flat &lt;strong&gt;string&lt;/strong&gt; set to localizable&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;The same grouped structure appearing on many pages&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A shared block with the image non-localizable and the alt text localizable&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Analytics identifiers or configuration values&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A flat &lt;strong&gt;string&lt;/strong&gt; set to non-localizable on the parent type&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A centrally managed promo banner or CTA&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A shared block, structural fields non-localizable, headline localizable&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A reasonable rule of thumb: if nobody can name two pages today where the same instance genuinely needs to update together, starting with flat properties keeps things simple. Moving to a shared block later is a one-time migration. Unwinding a shared block that turned out not to be shared is usually more work.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Practical Example: Analytics Container IDs&lt;/h2&gt;
&lt;p&gt;Consider a tracking container ID that needs to be present across the site.&lt;/p&gt;
&lt;p&gt;Placed inside an inline block within a Content Area, it gets copied into each language version when the variant is created. If that ID ever changes, someone needs to open every language version of every page holding it and update the field individually. On a site with ten languages and fifty pages, that is a lot of repetitive edits for a single value.&lt;/p&gt;
&lt;p&gt;Modeled as a flat property on the parent type with localizable set to false, the same change is made once in the master language and every language version reflects it.&lt;/p&gt;
&lt;p&gt;The useful distinction here is that a tracking ID is not translatable content at all. It is configuration. Recognising which of your fields are configuration rather than content often makes the modeling choice obvious.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;What Happens If an Author Deletes a Block&lt;/h2&gt;
&lt;p&gt;This question comes up on most projects, and the answer is more reassuring than teams often expect.&lt;/p&gt;
&lt;p&gt;Inline blocks do not appear in the global Trash, because they are not standalone items. Deleting one is closer to clearing a property value on the parent page. However, the parent page&#39;s version history does cover them.&lt;/p&gt;
&lt;p&gt;Every save and publish creates a revision in the Versions gadget, and rolling back to an earlier revision restores the Content Area as it was, including the inline blocks it contained. For everyday accidental deletions, this works well.&lt;/p&gt;
&lt;p&gt;There are limits worth knowing. The Versions gadget holds a &lt;strong&gt;20-version history&lt;/strong&gt;, so if a deletion goes unnoticed across many subsequent publishes, the revision containing the block can age out. The documentation is also explicit that content versions are not covered by trash management, meaning a version deleted directly from the gadget is permanently gone.&lt;/p&gt;
&lt;p&gt;Shared blocks behave differently here in a way that favours them: because they are standalone items, they do go to the Trash and can be restored independently of the pages referencing them.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;A Decision Matrix to Discuss With Your Team&lt;/h2&gt;
&lt;p&gt;This is offered as a conversation starter rather than a prescription. Your team&#39;s editorial workflow may well justify different choices.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Requirement&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Approach worth considering&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;background-color: rgb(236, 240, 241); text-align: left; vertical-align: top; border: 1px solid rgb(0, 0, 0); padding: 10px;&quot;&gt;
&lt;p&gt;&lt;strong&gt;Reasoning&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A fixed slot unique to one page&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Flat &lt;strong&gt;contentReference&lt;/strong&gt; and &lt;strong&gt;string&lt;/strong&gt; properties&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Structure stays locked, image can be shared across languages, alt text stays per language&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;The same grouped structure on many pages&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A shared block with mixed localizable settings&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;One instance referenced everywhere, with the SHARED badge making the reach visible&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Configuration values identical everywhere&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A flat &lt;strong&gt;string&lt;/strong&gt; set to non-localizable&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;One edit updates all languages&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Centrally managed reusable content&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A Content Area holding shared blocks&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Managed from the Assets panel, changes propagate to all referencing pages&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Genuinely flexible author-composed layouts&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;A Content Area holding inline blocks&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;padding: 10px; border: 1px solid rgb(0, 0, 0);&quot;&gt;
&lt;p&gt;Full composition freedom, with the naming convention and language copy flow documented for the team&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Questions Worth Asking Before You Model&lt;/h2&gt;
&lt;p&gt;Running through these with your technical and editorial teams tends to surface the right answer quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is the item count fixed by the design?&lt;/strong&gt; If the layout always calls for exactly three slots, flat properties express that more directly than an array.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should any field update everywhere when the master language changes?&lt;/strong&gt; If so, a flat property or a shared block will give you that. An inline block will not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is this content genuinely reused across pages today?&lt;/strong&gt; If no one can name two specific pages, starting simple is usually the safer bet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Are we planning to use an external translation service?&lt;/strong&gt; Stable named properties are generally easier for translation tooling to map reliably than nested arrays of anonymous items. This is worth validating with your specific vendor before committing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have editors been shown what the SHARED badge means?&lt;/strong&gt; A short walkthrough prevents a lot of surprises.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Have we agreed a naming convention?&lt;/strong&gt; Documenting it before the first content session is far easier than retrofitting it later.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Optimizely SaaS CMS gives teams a genuinely flexible toolkit here. Flat properties, embedded components, inline blocks, and shared blocks each serve a real purpose, and the localizable setting gives you fine-grained control over which values travel with translations and which stay anchored to one source of truth.&lt;/p&gt;
&lt;p&gt;If there is one idea worth carrying into your next modeling conversation, it is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A shared value needs a stable identity to attach to. Flat properties and shared blocks have one. Inline blocks, by design, do not.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Everything else tends to follow from that. Decide deliberately, write the decisions down, walk your editors through the SHARED badge, and test the language copy flow on a real page before go-live rather than after.&lt;/p&gt;
&lt;p&gt;I would genuinely like to hear how other teams are approaching this. Multilingual modeling is one of those areas where the best answer often depends on how a specific editorial team works day to day, and there is a lot to learn from comparing notes.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;How is your team handling Content Areas across languages?&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em class=&quot;_1gm2eq5 _4n13zc&quot;&gt;If you enjoyed this technical breakdown, you&amp;rsquo;ll like my monthly newsletter,&amp;nbsp;&lt;strong class=&quot;_1gm2eq5 lab8bo _4n13zc&quot;&gt;&lt;a class=&quot;_4n13zc _1l4j4nae _1q9ygi7 _4ivg92 _12zsz04 _4n13zc&quot; href=&quot;https://www.linkedin.com/newsletters/pragmatic-judgement-7487084233719549953/&quot;&gt;Pragmatic Judgement&lt;/a&gt;&lt;/strong&gt;. I write about where technical decisions meet real operational constraints for tech and delivery leaders.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-08-23T17:38:18.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Content Transfer coming to Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/matt-pallatt-is-not-a-developer/dates/2026/8/content-transfer-coming-to-saas/" /><id>&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;Back in June I released &lt;span style=&quot;color: rgb(224, 62, 45);&quot;&gt;&lt;a style=&quot;color: rgb(224, 62, 45);&quot; href=&quot;/link/9ad65182172c47689f5e21a8f01f781b.aspx&quot;&gt;Content Transfer&lt;/a&gt;&lt;/span&gt; on the Optimizely nuget feed &amp;ndash; today I&amp;rsquo;m &lt;span style=&quot;color: rgb(224, 62, 45);&quot;&gt;&lt;a style=&quot;color: rgb(224, 62, 45);&quot; href=&quot;https://nuget.optimizely.com/packages/mp.dxpcontenttransfer/&quot;&gt;releasing version 1.0&lt;/a&gt;&lt;/span&gt;, because we&amp;rsquo;ve had it running out in the wild for a while now and nobody has shouted that it&amp;rsquo;s done anything other than work as expected and hasn&amp;rsquo;t destroyed any content. So, I&amp;rsquo;m a happy boy.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;Related to this news, I&amp;rsquo;ve spent some of the last month porting a lot of that work over to Optimizely SaaS, working on the new OCP UI extension framework that&amp;rsquo;s being released later this year.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;Content Transfer SaaS&amp;nbsp;allows you to move content that you&#39;ve authored in one enviroment easily to one of your other environments, without the need of Admin&#39;s Export and Import tools. It works in almost exactly the same way as the PaaS tool &amp;ndash; albeit with a few extra bells and whistles for the SaaS experience &amp;ndash; in that you set your environments up (in the Connect Platform, as opposed to CMS settings), then wherever you are viewing content (experiences, pages, blocks, images...) in the CMS you have the option to send it to one of your other configured environments. Easy peasy.&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;You&amp;rsquo;ll hopefully have roughly the same tree across environments and so a like for like transfer will be possible, but if you don&amp;rsquo;t then Content Transfer will do its best to locate where the content is supposed to land, and if it gets it wrong (or right), it offers you the option to re-position your content into a completely different place if needed.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US; mso-no-proof: yes;&quot;&gt;&lt;!-- [if gte vml 1]&gt;&lt;v:shapetype id=&quot;_x0000_t75&quot; coordsize=&quot;21600,21600&quot;
 o:spt=&quot;75&quot; o:preferrelative=&quot;t&quot; path=&quot;m@4@5l@4@11@9@11@9@5xe&quot; filled=&quot;f&quot;
 stroked=&quot;f&quot;&gt;
 &lt;v:stroke joinstyle=&quot;miter&quot;/&gt;
 &lt;v:formulas&gt;
  &lt;v:f eqn=&quot;if lineDrawn pixelLineWidth 0&quot;/&gt;
  &lt;v:f eqn=&quot;sum @0 1 0&quot;/&gt;
  &lt;v:f eqn=&quot;sum 0 0 @1&quot;/&gt;
  &lt;v:f eqn=&quot;prod @2 1 2&quot;/&gt;
  &lt;v:f eqn=&quot;prod @3 21600 pixelWidth&quot;/&gt;
  &lt;v:f eqn=&quot;prod @3 21600 pixelHeight&quot;/&gt;
  &lt;v:f eqn=&quot;sum @0 0 1&quot;/&gt;
  &lt;v:f eqn=&quot;prod @6 1 2&quot;/&gt;
  &lt;v:f eqn=&quot;prod @7 21600 pixelWidth&quot;/&gt;
  &lt;v:f eqn=&quot;sum @8 21600 0&quot;/&gt;
  &lt;v:f eqn=&quot;prod @7 21600 pixelHeight&quot;/&gt;
  &lt;v:f eqn=&quot;sum @10 21600 0&quot;/&gt;
 &lt;/v:formulas&gt;
 &lt;v:path o:extrusionok=&quot;f&quot; gradientshapeok=&quot;t&quot; o:connecttype=&quot;rect&quot;/&gt;
 &lt;o:lock v:ext=&quot;edit&quot; aspectratio=&quot;t&quot;/&gt;
&lt;/v:shapetype&gt;&lt;v:shape id=&quot;Picture_x0020_1&quot; o:spid=&quot;_x0000_i1025&quot; type=&quot;#_x0000_t75&quot;
 style=&#39;width:145.8pt;height:237pt;visibility:visible;mso-wrap-style:square&#39;&gt;
 &lt;v:imagedata src=&quot;file:///C:/Users/MATTPA~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png&quot;
  o:title=&quot;&quot;/&gt;
&lt;/v:shape&gt;&lt;![endif]--&gt;&lt;!-- [if !vml]--&gt;&lt;img src=&quot;/link/ef28d5111496493c8e319ca932aaceaa.aspx&quot; width=&quot;194&quot; height=&quot;316&quot; border=&quot;0&quot; /&gt;&lt;!--[endif]--&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;As for bells and whistles, you can now transfer locales, content types, property groups and display templates as well as content!&lt;/span&gt;&lt;/p&gt;
&lt;p class=&quot;MsoNormal&quot;&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;All in all, I&#39;ve been quite impressed with the SaaS OCP UI extensions, and it has been remarkable easy to get my PaaS extension over into SaaS, and looking forwards to it being available on the OCP UI ecosystem - so n&lt;/span&gt;&lt;span style=&quot;mso-ansi-language: EN-US;&quot;&gt;ow we must wait patiently for Optimizely to release SaaS OCP UI extensions to the public (presumably around Opticon?) and hope that their pricing model for custom extensions isn&#39;t too eye watering.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</id><updated>2026-08-23T15:04:53.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Improving Optimizely Product Information in LLM Tools</title><link href="https://nicholas.sideras.net/blog/2026/08/21/improving-optimizely-information-in-llm-tools/" /><id>Tools like Claude Code can be great for working with Optimizely CMS, but when they index outdated or incomplete information about developing a website, it can lead to wasted time and money. Building a custom documentation skill can help LLM tools return correct answers about Optimizely products without constant course correction from the developer.</id><updated>2026-08-21T18:33:22.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>How to Get a Bacpac Out of DXP (And Never Do It By Hand Again)</title><link href="https://world.optimizely.com/blogs/kennyg/dates/2026/8/how-to-get-a-bacpac-out-of-dxp-and-never-do-it-by-hand-again/" /><id>&lt;p&gt;Every Optimizely DXP project eventually runs into the same task: getting production data onto a local machine so you can actually troubleshoot something. Real content. Real catalog data. Real edge cases that no seed script ever thinks to include.&lt;/p&gt;
&lt;p&gt;At first, most teams handle it the same way. Someone logs into the portal, downloads a couple of multi-gigabyte files, runs &lt;strong&gt;SqlPackage &lt;/strong&gt;from memory, and hopes nothing goes sideways.&lt;/p&gt;
&lt;p&gt;After doing that enough times myself, I automated the process by teaching an AI coding agent the entire workflow and packaging it as a Claude Code skill. This post walks through where bacpacs actually come from in DXP, how we retrieve them, and what the skill does once it has them.&lt;/p&gt;
&lt;h2&gt;Why bother with real data locally at all?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;The case for it&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Seed data lies to you.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s clean, it&#39;s small, and it never contains the malformed catalog entry or orphaned content reference that&#39;s sitting in production right now waiting to expose a problem you haven&#39;t seen yet. Restoring &lt;strong&gt;epicms &lt;/strong&gt;and &lt;strong&gt;epicommerce &lt;/strong&gt;from an actual DXP export gives you the real shape of the data: actual row counts, weird nulls, years-old content nobody has touched, and all the other surprises that come with a live system.&lt;/p&gt;
&lt;p&gt;The only downside is that the process is tedious enough that most teams don&#39;t do it nearly as often as they should. That&#39;s the part worth automating.&lt;/p&gt;
&lt;p&gt;This also turned out to be a great use case for an AI coding agent because the process is mostly orchestration. None of the individual steps are difficult, but there are enough of them that it&#39;s easy to miss one when you&#39;re doing it manually.&lt;/p&gt;
&lt;h2&gt;Where do bacpacs actually come from on DXP?&lt;/h2&gt;
&lt;h3&gt;1. The portal, by hand&lt;/h3&gt;
&lt;p&gt;DXP&#39;s PaaS portal has a Troubleshoot page for each project where you can request and download a database export directly. This is where most teams start: log in, select an environment, wait for the export, and download it.&lt;/p&gt;
&lt;p&gt;It works. It&#39;s also the slowest option because every step requires manual interaction.&lt;/p&gt;
&lt;h3&gt;2. The morning export, automated&lt;/h3&gt;
&lt;p&gt;On our project, a separate Azure DevOps release pipeline already generates fresh &lt;strong&gt;epicms&lt;/strong&gt; and &lt;strong&gt;epicommerce&lt;/strong&gt; bacpacs every morning and writes them into a DXP blob storage container.&lt;/p&gt;
&lt;p&gt;That container has a &lt;strong&gt;container-scoped SAS token&lt;/strong&gt; stored in the release definition variables with read and list permissions. As long as the token is refreshed periodically, grabbing the latest backups is as simple as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;./tools/get-dxp-backups.ps1
# -OutDir &amp;lt;dir&amp;gt; optional; defaults to Downloads&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No Entra ID login. No portal navigation. No API credentials.&lt;/p&gt;
&lt;p&gt;The script lists the container, finds the newest &lt;strong&gt;epicms*&lt;/strong&gt;&amp;nbsp;and &lt;strong&gt;epicommerce*&lt;/strong&gt; blobs, and downloads both.&lt;/p&gt;
&lt;p&gt;If your project already has a similar nightly export pipeline, it&#39;s worth looking through the release variables. You may already have everything you need.&lt;/p&gt;
&lt;h3&gt;3. A fresh export, on demand&lt;/h3&gt;
&lt;p&gt;Sometimes the overnight export isn&#39;t fresh enough. Maybe you&#39;re trying to verify whether today&#39;s deployment already ran, or you need data that changed recently.&lt;/p&gt;
&lt;p&gt;In those cases, the EpiCloud PowerShell module can trigger a brand-new export directly from a DXP environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Import-Module EpiCloud

$ctx = @{
    ClientKey = $key
    ClientSecret = $secret
    ProjectId = $projectId
}

$export = Start-EpiDatabaseExport `
    @ctx `
    -Environment &#39;Production&#39; `
    -DatabaseName &#39;epicms&#39; `
    -RetentionHours 24

# Poll Get-EpiDatabaseExport until status is &#39;Succeeded&#39;,
# then download $status.downloadLink&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach requires DXP API credentials created under &lt;strong&gt;Project &amp;rarr; Settings &amp;rarr; API&lt;/strong&gt; in the DXP portal.&lt;/p&gt;
&lt;p&gt;It also takes longer. A production-sized export can take 10 to 30 minutes before a file is even available to download.&lt;/p&gt;
&lt;p&gt;I generally only use this option when the nightly export isn&#39;t fresh enough.&lt;/p&gt;
&lt;h2&gt;So what does the skill actually do?&lt;/h2&gt;
&lt;p&gt;Once it has a &lt;strong&gt;.bacpac&lt;/strong&gt; for each database, the skill follows the same workflow every time:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Download&lt;/strong&gt; the newest pair of bacpacs and display their filenames, sizes, and timestamps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Display the plan&lt;/strong&gt; before making any changes, including which databases will be affected, which mode will be used, and what will be dropped.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request confirmation&lt;/strong&gt; when the planned action is destructive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drop the target databases&lt;/strong&gt; when running in replacement mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Import both databases in parallel&lt;/strong&gt; using &lt;strong&gt;SqlPackage /Action:Import&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify the results&lt;/strong&gt; using index health checks and sanity row counts against known tables, including &lt;strong&gt;tblContent&lt;/strong&gt; for CMS and &lt;strong&gt;CatalogEntry&lt;/strong&gt; for Commerce.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Report freshness information&lt;/strong&gt; using the source bacpac timestamps so you know exactly how current the restored data is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean up&lt;/strong&gt; the multi-gigabyte bacpac files after the imports have been confirmed successful.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Running the imports in parallel usually means the total time is roughly equal to whichever database takes longer instead of the combined time of both imports.&lt;/p&gt;
&lt;h2&gt;What can I tell it to do differently?&lt;/h2&gt;
&lt;p&gt;The skill accepts plain-language instructions and maps them to four independent decisions.&lt;/p&gt;
&lt;h3&gt;Scope&lt;/h3&gt;
&lt;p&gt;Restore both databases by default, or restore only:&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;font-weight: bold;&quot;&gt;&lt;strong&gt;cms-only&lt;/strong&gt;&lt;/li&gt;
&lt;li style=&quot;font-weight: bold;&quot;&gt;&lt;strong&gt;commerce-only&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Mode&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Replace (default)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This drops the existing &lt;strong&gt;epicms &lt;/strong&gt;and &lt;strong&gt;epicommerce &lt;/strong&gt;databases and restores into those same database names. Nothing else in your local configuration has to change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Additional&lt;/strong&gt;, also called &lt;em&gt;keep&lt;/em&gt; or &lt;em&gt;side-by-side&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This imports the bacpacs into new timestamped database names and leaves your existing databases untouched. It&#39;s useful when you&#39;re in the middle of something and don&#39;t want to disrupt whatever you&#39;re currently working on.&lt;/p&gt;
&lt;h3&gt;Source&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;blob (default)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This uses the automated morning export from blob storage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;fresh-export&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This generates a new export directly from DXP, optionally from Production, Preproduction, Integration, or another environment.&lt;/p&gt;
&lt;h3&gt;Confirmation&lt;/h3&gt;
&lt;p&gt;Destructive operations always request confirmation unless you explicitly say something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;q&gt;just do it&lt;/q&gt;&lt;/li&gt;
&lt;li&gt;&lt;q&gt;force&lt;/q&gt;&lt;/li&gt;
&lt;li&gt;&lt;q&gt;don&#39;t wait for confirmation&lt;/q&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So a request like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Refresh my local databases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;runs the full default workflow.&lt;/p&gt;
&lt;p&gt;While:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Restore commerce only from Preproduction and don&#39;t ask.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;does exactly that.&lt;/p&gt;
&lt;p&gt;Same skill. Same guardrails. Different scope.&lt;/p&gt;
&lt;h2&gt;How fast is this, really?&lt;/h2&gt;
&lt;p&gt;A production-sized import is still a heavy operation. Every index gets disabled before the bulk load and rebuilt afterward.&lt;/p&gt;
&lt;p&gt;The biggest performance improvements come from a few places.&lt;/p&gt;
&lt;h3&gt;Run both restores in parallel&lt;/h3&gt;
&lt;p&gt;This is already the default behavior. The CMS and Commerce imports run at the same time, so the total duration is based mostly on whichever import takes longer.&lt;/p&gt;
&lt;h3&gt;Tune SQL Server once&lt;/h3&gt;
&lt;p&gt;A few local SQL Server configuration changes can help every subsequent restore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure the &lt;strong&gt;model &lt;/strong&gt;database with simple recovery and appropriately sized files so new databases don&#39;t immediately run into autogrow stalls.&lt;/li&gt;
&lt;li&gt;Pre-size &lt;strong&gt;tempdb &lt;/strong&gt;to avoid unnecessary growth during the index rebuild phase.&lt;/li&gt;
&lt;li&gt;Increase max degree of parallelism so index rebuild operations can use the available CPU resources.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Monitor progress&lt;/h3&gt;
&lt;p&gt;Instead of staring at a blank terminal window, the bundled progress watcher follows the import logs and displays progress by phase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download&lt;/li&gt;
&lt;li&gt;Data import&lt;/li&gt;
&lt;li&gt;Index rebuild&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That doesn&#39;t turn a multi-gigabyte restore into a five-minute operation.&lt;/p&gt;
&lt;p&gt;It does mean the twenty-something minutes it takes no longer require constant supervision.&lt;/p&gt;
&lt;h2&gt;Worth stealing for your own project?&lt;/h2&gt;
&lt;p&gt;None of this is particularly Optimizely-specific.&lt;/p&gt;
&lt;p&gt;Under the covers, it&#39;s a blob storage container, an Azure DevOps pipeline variable, and a PowerShell wrapper around &lt;strong&gt;SqlPackage&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What makes it worthwhile on a DXP project is that almost every team already has this process. The problem is that it&#39;s often tribal knowledge. One person knows where the exports live. Another remembers the right &lt;strong&gt;SqlPackage &lt;/strong&gt;switches. Everybody else ends up asking the same questions every few months.&lt;/p&gt;
&lt;p&gt;Turning &lt;q&gt;How do I get fresh production data locally?&lt;/q&gt; into a reusable skill means that knowledge lives in the repository instead of in somebody&#39;s head. Anyone on the team can use it, and the process stays consistent every time.&lt;/p&gt;
&lt;p&gt;If your project already has a nightly export pipeline, go check the release variables before building anything from scratch. There&#39;s a decent chance you&#39;ve already got most of what you need.&lt;/p&gt;</id><updated>2026-08-20T20:59:49.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Error FK_tblContentSoftlink_tblPropertyDefinition in CMS</title><link href="https://world.optimizely.com/blogs/vinit-gavankar/dates/2026/8/error-fk_tblcontentsoftlink_tblpropertydefinition-in-cms/" /><id>&lt;p&gt;&lt;strong&gt;Example Error :&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;Microsoft.Data.SqlClient.SqlException (0x80131904): The DELETE statement conflicted with the REFERENCE constraint &quot;FK_tblContentSoftlink_tblPropertyDefinition&quot;. The conflict occurred in database &quot;XYZ-epicms&quot;, table &quot;dbo.tblContentSoftlink&quot;, column &#39;fkOwnerPropertyDefinitionID&#39;.&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;This is happening because you&amp;rsquo;re attempting to remove a property definition, and during model sync on initialization, it tries to delete that row from &lt;strong&gt;tblPropertyDefinition&lt;/strong&gt;. However, there are still rows in&amp;nbsp;&lt;strong&gt;tblContentSoftlink&lt;/strong&gt;&amp;nbsp;that reference this property definition.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;First, I will ask you to try &quot;Restart one instance at a time&quot; from the &quot;Troubleshoot&quot; tab of the&lt;strong&gt; PAAS Portal&lt;/strong&gt;&amp;nbsp;and check again.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;If the issue continues, this query will give you all property definitions that have rows in &lt;strong&gt;tblContentSoftlink&lt;/strong&gt;, along with their property definition type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT *&lt;br /&gt;FROM tblPropertyDefinition pd&lt;br /&gt;INNER JOIN tblPropertyDefinitionType pdtype&lt;br /&gt;    ON pdtype.pkid = pd.fkPropertyDefinitionTypeID&lt;br /&gt;WHERE EXISTS (&lt;br /&gt;    SELECT 1&lt;br /&gt;    FROM tblContentSoftlink cs&lt;br /&gt;    WHERE cs.fkOwnerPropertyDefinitionID = pd.pkid&lt;br /&gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you know the property definition to remove, you can delete the dependent softlink rows first (replace&amp;nbsp;&lt;code&gt;X&lt;/code&gt;&amp;nbsp;with the&amp;nbsp;&lt;code&gt;pkid&lt;/code&gt;&amp;nbsp;of the property definition):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DELETE FROM tblContentSoftlink&lt;br /&gt;WHERE fkOwnerPropertyDefinitionID = X;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;br /&gt;After that, the modelsync should be able to remove the property definition.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;I&amp;rsquo;d recommend testing this&amp;nbsp;&lt;strong&gt;locally first, using a copy of the preproduction database,&lt;/strong&gt;&amp;nbsp;to ensure it behaves as expected.&lt;/p&gt;</id><updated>2026-08-20T11:58:26.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Get count of pages, blocks , Assets from the CMS.</title><link href="https://world.optimizely.com/blogs/vinit-gavankar/dates/2026/8/get-count-of-pages-blocks--assets-from-the-cms/" /><id>&lt;p&gt;The following queries retrieve details from the CMS database. Make sure to try these queries locally first.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1 &amp;mdash; Discover your content type bases first:&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```&lt;br /&gt;SELECT Base, COUNT(*) AS TypeCount&lt;br /&gt;FROM tblContentType&lt;br /&gt;GROUP BY Base&lt;br /&gt;ORDER BY TypeCount DESC;&lt;br /&gt;```&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 2 &amp;mdash; Count all content grouped by base type (single query):&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```&lt;br /&gt;SELECT &lt;br /&gt;    ct.Base         AS ContentBase,&lt;br /&gt;    ct.Name         AS ContentTypeName,&lt;br /&gt;    COUNT(c.pkID)   AS ContentCount&lt;br /&gt;FROM tblContent c&lt;br /&gt;INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID&lt;br /&gt;GROUP BY ct.Base, ct.Name&lt;br /&gt;ORDER BY ct.Base, ContentCount DESC;&lt;br /&gt;```&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 3 &amp;mdash; Individual counts per category:&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```&lt;br /&gt;-- Pages&lt;br /&gt;SELECT COUNT(*) AS PageCount&lt;br /&gt;FROM tblContent c&lt;br /&gt;INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID&lt;br /&gt;WHERE ct.Base = &#39;Page&#39;;&lt;br /&gt;&lt;br /&gt;-- Blocks&lt;br /&gt;SELECT COUNT(*) AS BlockCount&lt;br /&gt;FROM tblContent c&lt;br /&gt;INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID&lt;br /&gt;WHERE ct.Base = &#39;Block&#39;;&lt;br /&gt;&lt;br /&gt;-- Images (adjust base value based on Step 1 results)&lt;br /&gt;SELECT COUNT(*) AS ImageCount&lt;br /&gt;FROM tblContent c&lt;br /&gt;INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID&lt;br /&gt;WHERE ct.Base = &#39;Image&#39;;&lt;br /&gt;&lt;br /&gt;-- Documents / Files&lt;br /&gt;SELECT COUNT(*) AS DocumentCount&lt;br /&gt;FROM tblContent c&lt;br /&gt;INNER JOIN tblContentType ct ON c.fkContentTypeID = ct.pkID&lt;br /&gt;WHERE ct.Base IN (&#39;File&#39;, &#39;Video&#39;, &#39;Media&#39;);&lt;br /&gt;```&lt;/code&gt;&lt;/pre&gt;</id><updated>2026-08-20T11:52:59.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely CMP Login Failure After Enabling SSO – The Certificate Mismatch That Locked Everyone Out</title><link href="https://madhuanbalagan.com/?p=5050" /><id>&lt;p&gt;Recently, our team faced a production issue where users suddenly lost access to Optimizely CMP, Opti ID Admin Center, and the Optimizely Support Portal. The&amp;#46;&amp;#46;&amp;#46;&lt;/p&gt;
&lt;p&gt;The post &lt;a href=&quot;https://madhuanbalagan.com/optimizely-cmp-login-failure-after-enabling-sso-the-certificate-mismatch-that-locked-everyone-out&quot;&gt;Optimizely CMP Login Failure After Enabling SSO – The Certificate Mismatch That Locked Everyone Out&lt;/a&gt; appeared first on &lt;a href=&quot;https://madhuanbalagan.com&quot;&gt;Madhu Anbalagan&amp;#039;s Blog&lt;/a&gt;.&lt;/p&gt;
</id><updated>2026-08-19T01:45:00.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Upgrade Optimizely CMS 12 to .NET 10</title><link href="https://www.gulla.net/en/blog/upgrade-optimizely-cms-12-to-net10/" /><id>If you are still running CMS 12 on .NET 8, it&#39;s time to upgrade!</id><updated>2026-08-17T11:57:17.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>What Your Optimizely DXP Is Serving to AI Crawlers - and the Three Levers You Actually Have</title><link href="https://pino-labs.com/blog/optimizely-dxp-ai-crawlers-three-levers/" /><id>Unblocked bot traffic on Optimizely DXP is a billable page view, and the Cloudflare zone in front of your site is not yours to configure. Three levers remain: robots.txt, Optimizely Support, and bringing your own Cloudflare zone (Orange-to-Orange) for AI Crawl Control. Plus the trap in Cloudflare&#39;s July 2026 change, where blocking Training also blocks Googlebot, Applebot and BingBot.</id><updated>2026-08-17T00:00:00.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Migrating from AEM to Optimizely SaaS CMS: Solving the Architectural Hurdles</title><link href="https://world.optimizely.com/blogs/vipin-banka--learnings--insights/dates/2026/8/migrating-from-aem-to-optimizely-saas-cms-solving-the-architectural-hurdles/" /><id>&lt;p&gt;Migrating a digital footprint from Adobe Experience Manager (AEM) to Optimizely SaaS CMS is a major milestone for any enterprise modernizing its digital experience platform. When you are dealing with years of accumulated legacy content spanning a large number of pages, it is tempting to treat this purely as a volume problem.&lt;/p&gt;
&lt;p&gt;In reality, bulk loading data is the easy part. The real challenge lies in the architectural differences between how AEM and Optimizely SaaS CMS represent content structure, localization, and identity.&lt;/p&gt;
&lt;p&gt;In this article, we will bypass the generic advice and focus on the high-stakes architectural hurdles you will encounter during an AEM-to-Optimizely migration &amp;mdash; and how to solve them programmatically using the Optimizely SaaS CMS REST API.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The Sanity Checks: Start Simple&lt;/h2&gt;
&lt;p&gt;Before writing migration scripts or converting templates, always begin with a few basic sanity checks on your source JCR (Java Content Repository) data. Running these early prevents major pipeline failures later:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clean up the ROT&lt;/strong&gt;: Run an analysis in AEM to find Redundant, Outdated, and Trivial content. Look for pages with zero traffic over the last 12 months, expired pages (where &lt;strong&gt;offTime&lt;/strong&gt; is in the past), or duplicate assets. Retiring this content before migrating saves significant engineering, QA, and content-review effort.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify target locales&lt;/strong&gt;: Make sure all languages used in your AEM tree are explicitly registered in your Optimizely SaaS CMS instance before pushing content. Attempting to load localized content into an unregistered locale will cause API errors.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once your basic environment is ready, you can tackle the primary architectural shifts.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Hurdle 1: Parallel Trees vs. The Single-Node Localization Model&lt;/h2&gt;
&lt;p&gt;One of the deepest conceptual mismatches between AEM and Optimizely is how they handle multi-language websites.&lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;AEM organizes languages using parallel folder trees. Under &lt;strong&gt;/content/mysite&lt;/strong&gt;, you might have &lt;strong&gt;/en&lt;/strong&gt; for English and &lt;strong&gt;/fr&lt;/strong&gt; for French. These are completely separate nodes in the JCR, linked together externally via Multi-Site Manager (MSM) or Language Copy. If a French page exists under &lt;strong&gt;/content/mysite/fr/about&lt;/strong&gt; but has no English equivalent under &lt;strong&gt;/content/mysite/en/about&lt;/strong&gt;, AEM is perfectly happy.&lt;/p&gt;
&lt;p&gt;Optimizely SaaS CMS uses a single-node localization model. A content item is represented by a single unique key. That single node has a &lt;strong&gt;primaryLocale&lt;/strong&gt; &amp;mdash; the master language &amp;mdash; established permanently at creation. Any additional languages (such as French) exist as locale branches hanging off that same single node.&lt;/p&gt;
&lt;p&gt;If your client&#39;s stated intent is that English must be the master language, but your discovery analysis reveals that a meaningful portion of regional pages or folders have no English equivalent, you have a structural conflict. You cannot create a French translation branch in Optimizely if the master English node does not exist.&lt;/p&gt;
&lt;h3&gt;The Solution: The Unpublished English Draft Shell&lt;/h3&gt;
&lt;p&gt;To keep a consistent English-master model without publishing placeholder or machine-translated English content that regional teams never requested, use the &lt;strong&gt;Unpublished English Draft Shell&lt;/strong&gt; pattern.&lt;/p&gt;
&lt;p&gt;When your migration pipeline encounters a French page with no English sibling, the loader should:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;Create the content item with English (&lt;strong&gt;en&lt;/strong&gt;) set as the master locale, carrying only non-localized properties and a fallback display name. Keep this version as an unpublished draft.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the French version (&lt;strong&gt;fr&lt;/strong&gt;) as a translation branch on that same key and publish it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the two-step API flow:&lt;/p&gt;
&lt;h4&gt;Step 1: Create the master item in English (Unpublished Draft)&lt;/h4&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;POST https://api.cms.optimizely.com/v1/content
Content-Type: application/json

{
  &quot;key&quot;: &quot;5f2b9c1e-4a7d-4b8e-9c0d-1a2b3c4d5e6f&quot;,
  &quot;contentType&quot;: &quot;standardPage&quot;,
  &quot;container&quot;: &quot;parent-container-guid&quot;,
  &quot;initialVersion&quot;: {
    &quot;displayName&quot;: &quot;About Us (FR Source &amp;mdash; Master Shell)&quot;,
    &quot;locale&quot;: &quot;en&quot;,
    &quot;properties&quot;: {
      &quot;heading&quot;: { &quot;value&quot;: &quot;Placeholder English Title&quot; }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 2: Post the real French version to the same key&lt;/h4&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;POST https://api.cms.optimizely.com/v1/content/5f2b9c1e-4a7d-4b8e-9c0d-1a2b3c4d5e6f/versions
Content-Type: application/json

{
  &quot;displayName&quot;: &quot;&amp;Agrave; propos&quot;,
  &quot;locale&quot;: &quot;fr&quot;,
  &quot;properties&quot;: {
    &quot;heading&quot;: { &quot;value&quot;: &quot;&amp;Agrave; propos de nous&quot; }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After verifying the version was created, publish it:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;POST https://api.cms.optimizely.com/v1/content/5f2b9c1e-4a7d-4b8e-9c0d-1a2b3c4d5e6f/versions/1:publish&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures your public-facing site remains clean, while maintaining a consistent and governable data model behind the scenes. Editors will see an empty English draft in the CMS &amp;mdash; make sure your editorial team is briefed on this pattern so it does not cause confusion post-migration.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Hurdle 2: Making the Migration Re-runnable with Deterministic Keys&lt;/h2&gt;
&lt;p&gt;Migrations are rarely a one-and-done big bang. You will run dry runs, test waves, and delta syncs.&lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;If your migration loader relies on the target CMS to auto-generate random IDs upon creation, you lose idempotency immediately. If a script fails halfway through, running it again will result in duplicate pages, orphaned assets, and broken references. Maintaining a large lookup table mapping old AEM paths to new random GUIDs is fragile and prone to synchronization errors across environments.&lt;/p&gt;
&lt;h3&gt;The Solution: Deterministic Key Generation (UUID v5)&lt;/h3&gt;
&lt;p&gt;Optimizely SaaS CMS allows you to supply your own &lt;strong&gt;key&lt;/strong&gt; (a valid UUID/GUID format) inside the request body when creating content.&lt;/p&gt;
&lt;p&gt;You can exploit this by generating a deterministic UUID v5. UUID v5 uses a SHA-1 hash combining a fixed namespace UUID with a unique input string. In your migration pipeline, use AEM&#39;s highly stable &lt;strong&gt;jcr:uuid&lt;/strong&gt; as the input name:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;optimizely_key = uuidv5(MIGRATION_NAMESPACE_UUID, aem_jcr_uuid)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because &lt;strong&gt;jcr:uuid&lt;/strong&gt; is unique and survives page moves or renames inside AEM, your migration pipeline will generate the exact same Optimizely key every single time it processes a given item &amp;mdash; on any machine, in any environment, with no coordination required.&lt;/p&gt;
&lt;p&gt;This gives you native idempotency:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;If the item already exists in Optimizely, your script performs a &lt;strong&gt;PATCH&lt;/strong&gt; update.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it does not exist, it performs a &lt;strong&gt;POST&lt;/strong&gt; to create it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you need to roll back a wave, your script knows exactly which keys to target for hard deletion.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Hurdle 3: Content Type Immutability and the &quot;Reparent &amp;amp; Swap&quot; Pattern&lt;/h2&gt;
&lt;p&gt;During discovery, you classify AEM pages into Optimizely content types &amp;mdash; some remain Pages, others (such as Experience Fragments or non-routed pages) become Shared Blocks.&lt;/p&gt;
&lt;h3&gt;The Problem&lt;/h3&gt;
&lt;p&gt;What happens if you load an item as a Page, and the editorial team identifies during UAT that it should have been a Shared Block?&lt;/p&gt;
&lt;p&gt;In Optimizely, an item&#39;s &lt;strong&gt;contentType&lt;/strong&gt; is immutable once created. You cannot change a Page to a Block because they inherit from different base classes and are stored differently by the platform &amp;mdash; Pages have URL routing and hierarchy placement, Blocks do not. There is no API endpoint that converts one to the other.&lt;/p&gt;
&lt;p&gt;Furthermore, if that incorrectly typed item is a parent container with a large number of correct children sitting beneath it, a hard delete of the parent will cascade and destroy all those child nodes.&lt;/p&gt;
&lt;h3&gt;The Solution: The &quot;Reparent &amp;amp; Swap&quot; Pattern&lt;/h3&gt;
&lt;p&gt;Rather than destroying and rebuilding the entire child hierarchy, execute an architectural swap via the REST API:&lt;/p&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a temporary holding folder&lt;/strong&gt; elsewhere in your content tree.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Move all children&lt;/strong&gt; by updating each child&#39;s &lt;strong&gt;container&lt;/strong&gt; property via a &lt;strong&gt;PATCH&lt;/strong&gt; request to point to the temporary holder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hard-delete the now-empty parent&lt;/strong&gt;, passing the special &lt;strong&gt;cms-permanent-delete&lt;/strong&gt; header to release the key immediately rather than moving it to the Trash:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;DELETE https://api.cms.optimizely.com/v1/content/bad-parent-guid
cms-permanent-delete: true&lt;/code&gt;&lt;/pre&gt;
&lt;ol class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recreate the parent&lt;/strong&gt; using the exact same GUID key, this time supplying the correct &lt;strong&gt;contentType&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Move the children back&lt;/strong&gt; by patching each child&#39;s &lt;strong&gt;container&lt;/strong&gt; to point to the original GUID.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hard-delete the temporary folder&lt;/strong&gt; to leave no trace.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This pattern preserves every child&#39;s GUID, version history, regional locale branches, and any inbound content references from other pages &amp;mdash; because none of that data was touched.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Architectural Hurdle 4: Applying the Swap Pattern to Language Versions&lt;/h2&gt;
&lt;p&gt;The Reparent &amp;amp; Swap concept extends naturally to localization issues, and this is where it becomes especially powerful during large migrations. There are two distinct language-version scenarios you are likely to encounter.&lt;/p&gt;
&lt;h3&gt;Scenario A: The &quot;Locale Swap&quot; &amp;mdash; Changing the Master Language of a GUID&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;primaryLocale&lt;/strong&gt; of a content item is structurally immutable in Optimizely. If you created a page with English as the master, you cannot call an API to change it to French. However, if the business requires a specific GUID to have French as its master language, you can execute a Locale Swap using a temporary item &amp;mdash; preserving the original GUID so all inbound links remain intact.&lt;/p&gt;
&lt;h4&gt;The Process&lt;/h4&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code&gt;[Original: GUID_A]              [Temp: GUID_T]             [Restored: GUID_A]
  Master: EN                      Master: FR (copy)          Master: FR
  Branch: FR                      Branch: EN (copy)          Branch: EN
        │                               ▲                          ▲
        └── Read all properties ────────┘                          │
        └── Hard-delete GUID_A                                     │
        └── Recreate GUID_A with FR as master ─────────────────────┘
        └── Hard-delete GUID_T&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;The API Steps&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt; &amp;mdash; Read all version properties for both &lt;strong&gt;en&lt;/strong&gt; and &lt;strong&gt;fr&lt;/strong&gt; from the original item and hold them in your script.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt; &amp;mdash; Create a temporary item with French as the &lt;strong&gt;primaryLocale&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;POST https://api.cms.optimizely.com/v1/content
{
  &quot;key&quot;: &quot;temp-guid&quot;,
  &quot;contentType&quot;: &quot;standardPage&quot;,
  &quot;container&quot;: &quot;temp-holding-folder-guid&quot;,
  &quot;initialVersion&quot;: {
    &quot;displayName&quot;: &quot;Temp Shell&quot;,
    &quot;locale&quot;: &quot;fr&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt; &amp;mdash; Hard-delete the original item to release GUID_A:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;DELETE https://api.cms.optimizely.com/v1/content/GUID_A
cms-permanent-delete: true&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt; &amp;mdash; Recreate GUID_A with French as the master locale and all original French content restored.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt; &amp;mdash; Create the English translation branch on GUID_A with the original English content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt; &amp;mdash; Publish according to the original publication state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt; &amp;mdash; Hard-delete the temporary item.&lt;/p&gt;
&lt;p&gt;The result: GUID_A now has French as its &lt;strong&gt;primaryLocale&lt;/strong&gt;, English as its translation branch, and every page in the system that referenced GUID_A is completely unaware that anything changed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;Scenario B: The &quot;Branch Swap&quot; &amp;mdash; Moving a Translation to the Correct Parent&lt;/h3&gt;
&lt;p&gt;In large migrations, AEM&#39;s parallel language trees can be misaligned during the bulk load &amp;mdash; the French version of Page X ends up attached to Page Y as a locale branch instead. Since locale branches are bound to a specific GUID, you cannot directly reparent them. You must use a Read-Write-Delete approach.&lt;/p&gt;
&lt;h4&gt;The API Steps&lt;/h4&gt;
&lt;p&gt;Assume French (&lt;strong&gt;fr&lt;/strong&gt;) is incorrectly sitting on &lt;strong&gt;Page_A&lt;/strong&gt; but belongs on &lt;strong&gt;Page_B&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt; &amp;mdash; Read the French version&#39;s full property data from Page_A:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;GET https://api.cms.optimizely.com/v1/content/Page_A/versions?locale=fr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt; &amp;mdash; Create a new French version branch on Page_B using the properties retrieved in Step 1:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;POST https://api.cms.optimizely.com/v1/content/Page_B/versions
Content-Type: application/json

{
  &quot;displayName&quot;: &quot;French Title&quot;,
  &quot;locale&quot;: &quot;fr&quot;,
  &quot;properties&quot;: { ... }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt; &amp;mdash; Publish the new French branch on Page_B if the original was published:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;POST https://api.cms.optimizely.com/v1/content/Page_B/versions/{newVersionId}:publish&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt; &amp;mdash; Delete the incorrect French locale branch from Page_A. Locale branch deletion is immediate and irreversible &amp;mdash; it does not go to Trash &amp;mdash; which is exactly the behavior you need here:&lt;/p&gt;
&lt;pre class=&quot;code-block&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;DELETE https://api.cms.optimizely.com/v1/content/Page_A/locales/fr&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important note on locale deletion:&lt;/strong&gt; Because deleting a locale branch is permanent with no recovery path, always verify that Step 2 and Step 3 have succeeded and the content is correctly visible in the target before executing Step 4. Build a mandatory verification check into your migration script before it fires the delete.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Building These Patterns into Your Migration Toolbox&lt;/h2&gt;
&lt;p&gt;All four patterns described above share a common foundation:&lt;/p&gt;
&lt;ul class=&quot;tight&quot;&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A Migration State Database&lt;/strong&gt; that tracks every AEM node&#39;s original &lt;strong&gt;jcr:uuid&lt;/strong&gt;, its deterministic Optimizely key, its content archetype, and its load status.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A purge/rollback mode&lt;/strong&gt; in your loader that can fire &lt;strong&gt;cms-permanent-delete&lt;/strong&gt; against a wave&#39;s key set &amp;mdash; gated behind an explicit flag and environment check so it can never be accidentally pointed at production.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structured logging with a run ID&lt;/strong&gt; on every API call, so any partial failure can be diagnosed and resumed rather than restarted.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When these utility scripts exist from day one of your migration build, structural corrections that would otherwise cost days of manual recovery become a 20-minute scripted operation.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The architectural gap between AEM and Optimizely SaaS CMS is real, but it is entirely solvable with deliberate engineering. By designing your localization strategy around a consistent master language model, generating deterministic UUIDs to make your pipeline re-runnable, treating content type immutability as a design constraint rather than a blocker, and having the Locale Swap and Branch Swap patterns ready as operational utilities, you give your migration the resilience it needs to handle edge cases without unplanned rework.&lt;/p&gt;
&lt;p&gt;Have you encountered similar structural challenges during platform migrations? Drop your experience in the comments below &amp;mdash; the community would love to hear how you approached them.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em class=&quot;_1gm2eq5 _4n13zc&quot;&gt;If you enjoyed this technical breakdown, you&amp;rsquo;ll like my monthly newsletter, &lt;strong class=&quot;_1gm2eq5 lab8bo _4n13zc&quot;&gt;&lt;a class=&quot;_4n13zc _1l4j4nae _1q9ygi7 _4ivg92 _12zsz04 _4n13zc&quot; href=&quot;https://www.linkedin.com/newsletters/pragmatic-judgement-7487084233719549953/&quot;&gt;Pragmatic Judgement&lt;/a&gt;&lt;/strong&gt;. I write about where technical decisions meet real operational constraints for tech and delivery leaders.&lt;/em&gt;&lt;/p&gt;</id><updated>2026-08-16T04:25:04.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>You’re not headless. You’re hybrid — and that’s the CMS 12 default.</title><link href="https://world.optimizely.com/blogs/hanskumar-tripathi/dates/2026/8/youre-not-headless.-youre-hybrid--and-thats-the-cms-12-default" /><id>&lt;p&gt;&lt;em&gt;For CMS 12 / Commerce teams with a React (or similar) storefront who keep hearing &amp;ldquo;just go headless&amp;rdquo; or &amp;ldquo;just move to Graph.&amp;rdquo; This is &lt;strong&gt;Part 1&lt;/strong&gt; of &lt;strong&gt;Find &amp;rarr; Graph: the hybrid storefront&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optimizely CMS 12 sites that look &amp;ldquo;headless&amp;rdquo; in the browser are usually hybrid.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Editors still work in the CMS. Catalog still lives in Commerce. Preview and on-page edit still need a server-rendered page. React (or similar) is a storefront shell, not the system of record.&lt;/p&gt;
&lt;p&gt;If you skip that, CMS 13 + Optimizely Graph feels like a frontend rewrite. It isn&amp;rsquo;t. Graph replaces Search &amp;amp; Navigation (Find) as the query/index layer.&lt;/p&gt;
&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; replace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content types and &lt;code&gt;IContentRepository&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Commerce catalog, pricing, and cart&lt;/li&gt;
&lt;li&gt;Edit mode and preview&lt;/li&gt;
&lt;li&gt;Your React / SSR host&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Three layers people mix up&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;CMS 12 today&lt;/th&gt;
&lt;th&gt;After Graph / CMS 13&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authoring&lt;/td&gt;
&lt;td&gt;CMS UI, content types&lt;/td&gt;
&lt;td&gt;Still CMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storefront&lt;/td&gt;
&lt;td&gt;Razor + React / Content Delivery API&lt;/td&gt;
&lt;td&gt;Same hybrid, or more CDA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search and listings&lt;/td&gt;
&lt;td&gt;Find (&lt;code&gt;IClient&lt;/code&gt;, facets, conventions)&lt;/td&gt;
&lt;td&gt;Optimizely Graph&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Content Delivery API is &lt;strong&gt;content over HTTP&lt;/strong&gt;. Find/Graph is &lt;strong&gt;query, facet, rank, and index&lt;/strong&gt;. Using CDA as your product-listing search is how teams paint themselves into a corner.&lt;/p&gt;
&lt;h2&gt;A hybrid shape that survives Graph&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;CMS/Commerce owns content and catalog.&lt;/li&gt;
&lt;li&gt;A thin .NET host does auth, cart, preview, and first paint (SSR).&lt;/li&gt;
&lt;li&gt;React hydrates listing / PDP / checkout islands.&lt;/li&gt;
&lt;li&gt;Listings talk to a query module (&lt;code&gt;IProductSearch&lt;/code&gt;, &lt;code&gt;IPageSearch&lt;/code&gt;) &amp;mdash; not &lt;code&gt;IClient&lt;/code&gt; in 40 controllers.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That last point &lt;em&gt;is&lt;/em&gt; the migration. Find behind a port &amp;rarr; Graph is an adapter. Find in every controller &amp;rarr; Graph is a rewrite.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public interface IProductSearch
{
    Task&amp;lt;ProductSearchResult&amp;gt; SearchAsync(
        ProductSearchRequest request,
        CancellationToken cancellationToken);
}

// CMS 12: Find adapter implements this.
// CMS 13: Graph adapter implements this.
// Controllers and React never take a dependency on IClient.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Do this before&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Draw the line: CDA vs Find vs &lt;code&gt;IContentLoader&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Put Find behind interfaces (search, facets, paging).&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t let React own search URLs the server doesn&amp;rsquo;t know.&lt;/li&gt;
&lt;li&gt;Measure three numbers: time from publish until the listing shows the new item in Find; listing p95; percent of searches with zero hits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is &lt;strong&gt;Part 1&lt;/strong&gt; of &lt;strong&gt;Find &amp;rarr; Graph: the hybrid storefront&lt;/strong&gt;. Next: typed Find query builders, facets, and &lt;code&gt;GetContentResultAsync&lt;/code&gt; &amp;mdash; so a Graph adapter can sit beside them later.&lt;/p&gt;</id><updated>2026-08-13T14:38:03.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Stott Security for Optimizely SaaS CMS</title><link href="https://world.optimizely.com/blogs/mark-stott/dates/2026/8/stott-security-for-optimizely-saas-cms/" /><id>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.stott.pro/article/getting-started-with-stott-security&quot;&gt;Stott Security&lt;/a&gt;&amp;nbsp;has been helping developers and editors manage Content Security Policies and security headers on&amp;nbsp;&lt;strong&gt;Optimizely PaaS CMS&lt;/strong&gt;&amp;nbsp;for several years. Today I am pleased to share an early access release of&amp;nbsp;&lt;strong&gt;Stott Security for Optimizely SaaS CMS&lt;/strong&gt;: a complete rebuild designed for Optimizely&amp;rsquo;s composable platform, built on the&amp;nbsp;&lt;strong&gt;Optimizely Connect Platform (OCP)&lt;/strong&gt;&amp;nbsp;and rendered inside the CMS using&amp;nbsp;&lt;strong&gt;CMS UI Extensions&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;A Complete Rebuild&lt;/h2&gt;
&lt;p&gt;Optimizely SaaS CMS does not give you a web server to install a NuGet package into, so the add-on model that has served the PaaS version so well simply does not exist there. Instead, extensions are built as apps on the Optimizely Connect Platform and surface their interfaces inside the CMS using CMS UI Extensions. Stott Security for SaaS is exactly that: an OCP app that adds a full-page security console to your CMS and serves your compiled headers to your site&amp;rsquo;s front end from a public endpoint.&lt;/p&gt;
&lt;p&gt;Everything about this implementation lives in OCP, and the data belongs to your installation.&amp;nbsp;&lt;strong&gt;Entity Framework and SQL Server are gone&lt;/strong&gt;; configuration is now held in OCP&amp;rsquo;s key value storage. This is a much simpler data store, and it does mean some functional changes &amp;mdash; relational features of the PaaS version such as the audit history and per-page policies have not carried across. I have set out an honest comparison in the feature parity table below.&lt;/p&gt;
&lt;p&gt;What has carried across is the engine. The header compilation engine is a faithful TypeScript port of the C# engine that powers the PaaS add-on, cross-validated against the original so that both products produce the same output. That includes the automatic optimisation and splitting of oversized Content Security Policies across multiple headers so that your policy stays within browser and webserver limits.&lt;/p&gt;
&lt;p&gt;The rebuild was also an opportunity to add something the PaaS version has never had: a&amp;nbsp;&lt;strong&gt;draft and publish workflow&lt;/strong&gt;. Changes are saved as a draft, and nothing reaches your site until you press&amp;nbsp;&lt;strong&gt;Publish&lt;/strong&gt;. Publishing compiles your configuration once and stores the result, so the endpoint your front end calls is a single fast read with no compilation on the hot path. The console always shows you when the live configuration went live and whether unpublished changes exist.&lt;/p&gt;
&lt;h2&gt;What You Get Today&lt;/h2&gt;
&lt;p&gt;The console is split into four tabs:&amp;nbsp;&lt;strong&gt;Response headers&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;Content Security Policy&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;Preview&lt;/strong&gt;&amp;nbsp;and&amp;nbsp;&lt;strong&gt;Tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;&lt;strong&gt;Response headers&lt;/strong&gt;&amp;nbsp;tab covers eight standard security headers &amp;mdash; including X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Strict-Transport-Security and the Cross-Origin family &amp;mdash; each with a plain-English description, a behaviour of&amp;nbsp;&lt;strong&gt;Add&lt;/strong&gt;,&amp;nbsp;&lt;strong&gt;Remove&lt;/strong&gt;&amp;nbsp;or&amp;nbsp;&lt;strong&gt;Disabled&lt;/strong&gt;, and a preview of exactly what will be emitted. You can also add your own custom headers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.stott.pro/assets/stott-security-saas-response-headers-screen.png&quot; alt=&quot;The Response headers tab of Stott Security for Optimizely SaaS CMS, showing standard security headers with behaviour, value and preview options.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;&lt;strong&gt;Content Security Policy&lt;/strong&gt; tab uses the same source-first model as the PaaS version: you manage a list of sources &amp;mdash; domains, schemes, keywords, hashes and the &lt;strong&gt;&#39;nonce-random&#39;&lt;/strong&gt;&amp;nbsp;placeholder &amp;mdash; and grant each source the directives it needs. The Settings section lets you run the policy in report-only mode, upgrade insecure requests and send violation reports to an external collector, while the Sandbox section provides switches for the fifteen sandbox permissions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.stott.pro/assets/stott-security-saas-csp-sources-screen.png&quot; alt=&quot;The Content Security Policy Sources tab of Stott Security for Optimizely SaaS CMS, showing sources and the directives granted to each.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The&amp;nbsp;&lt;strong&gt;Preview&lt;/strong&gt;&amp;nbsp;tab shows your headers in three views:&amp;nbsp;&lt;strong&gt;Pending&lt;/strong&gt;&amp;nbsp;shows what publishing your current draft would produce,&amp;nbsp;&lt;strong&gt;Live&lt;/strong&gt;&amp;nbsp;shows what your site&amp;rsquo;s head is being served right now, and&amp;nbsp;&lt;strong&gt;Integration&lt;/strong&gt;&amp;nbsp;provides the endpoint for your installation together with a worked example of applying the headers. A diagnostics panel also sits above the console, warning you when a policy is approaching size limits, has been split, or has been dropped entirely.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.stott.pro/assets/stott-security-saas-preview-screen.png&quot; alt=&quot;The Preview tab of Stott Security for Optimizely SaaS CMS, showing the live headers currently being served to the site head.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finally, the&amp;nbsp;&lt;strong&gt;Tools&lt;/strong&gt;&amp;nbsp;tab provides import and export of the complete configuration as JSON. This matters more on SaaS than it ever did on PaaS: the configuration is held by the app installation, so uninstalling the app deletes it. Keep an exported copy somewhere outside of Optimizely.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.stott.pro/assets/stott-security-saas-tools-screen.png&quot; alt=&quot;The Tools tab of Stott Security for Optimizely SaaS CMS, showing the export of the complete configuration as JSON.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Feature Parity&lt;/h2&gt;
&lt;p&gt;A complete rebuild on a much simpler data store means this is not a feature-for-feature copy of the PaaS add-on. Some features have not carried across, some are on the roadmap, and a couple of things are new to the SaaS product.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PaaS&lt;/th&gt;
&lt;th&gt;SaaS&lt;/th&gt;
&lt;th&gt;SaaS Roadmap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSP sources &amp;amp; directives&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSP Keyword, scheme, hash &amp;amp; nonce sources&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSP Report-only mode &amp;amp; upgrade insecure requests&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSP sandbox&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSP Automatic policy splitting &amp;amp; optimisation&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External violation reporting&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internal violation reporting (Violations tab)&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote CSP Allow List&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-page CSP sources&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard &amp;amp; custom response headers&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CORS settings&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions Policy&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security.txt files&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global / Application / Host contexts&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;Global only&lt;/td&gt;
&lt;td&gt;Planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Header preview&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Draft &amp;amp; publish workflow&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit history&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✘&lt;/td&gt;
&lt;td&gt;No current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import &amp;amp; export&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headless headers API&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;✔&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headless middleware for your head&lt;/td&gt;
&lt;td&gt;Build your own&lt;/td&gt;
&lt;td&gt;Build your own&lt;/td&gt;
&lt;td&gt;Planned package&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The biggest omissions follow directly from the storage model: without relational data there is no audit trail, and violation reports are sent to an external collector rather than being stored and summarised inside the CMS. The draft and publish workflow, together with the export tooling, are the counterweights &amp;mdash; you can always see what is live and when it went live, and you can keep copies of your configuration outside the platform.&lt;/p&gt;
&lt;p&gt;On the roadmap, Application and Host level contexts are a firm commitment &amp;mdash; the storage model was designed for them from day one &amp;mdash; alongside Permissions Policy management and a distributable middleware package for your front end.&lt;/p&gt;
&lt;h2&gt;Plugging It Into Your Headless Solution&lt;/h2&gt;
&lt;p&gt;Every Optimizely SaaS CMS solution is a headless solution, so applying the headers is always the responsibility of your front end. Your head requests the compiled headers from the endpoint shown on the&amp;nbsp;&lt;strong&gt;Integration&lt;/strong&gt; view of the Preview tab and applies them to its responses.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;An example response looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-JSON&quot;&gt;{
    &quot;headers&quot;: [
        {
            &quot;key&quot;: &quot;Content-Security-Policy&quot;,
            &quot;value&quot;: &quot;default-src &#39;self&#39;;...&quot;,
            &quot;isRemoval&quot;: false,
            &quot;isReplacement&quot;: false
        },
        {
            &quot;key&quot;: &quot;X-Content-Type-Options&quot;,
            &quot;value&quot;: &quot;nosniff&quot;,
            &quot;isRemoval&quot;: false,
            &quot;isReplacement&quot;: true
        },
        {
            &quot;key&quot;: &quot;server&quot;,
            &quot;value&quot;: &quot;&quot;,
            &quot;isRemoval&quot;: true,
            &quot;isReplacement&quot;: false
        }
    ],
    &quot;publishedAt&quot;: &quot;2026-08-12T00:06:28.000Z&quot;,
    &quot;cacheSeconds&quot;: 300
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each entry in the headers collection uses the same structure as the PaaS Header Listing API, so a consumer only needs to implement three behaviours:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When&amp;nbsp;&lt;strong&gt;isRemoval&lt;/strong&gt; is &lt;strong&gt;true&lt;/strong&gt;:&amp;nbsp;remove the header from your response.&lt;/li&gt;
&lt;li&gt;When&amp;nbsp;&lt;strong&gt;isReplacement&lt;/strong&gt; is &lt;strong&gt;true&lt;/strong&gt;:&amp;nbsp;set the header, replacing any existing value.&lt;/li&gt;
&lt;li&gt;Otherwise: append the header to your response. The Content Security Policy is served this way because a large policy may legitimately be split across multiple headers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your front end has two further responsibilities: replace the &lt;strong&gt;&#39;nonce-random&#39;&lt;/strong&gt;&amp;nbsp;placeholder with a fresh value on every request, and add the &lt;strong&gt;nonce=&quot;random&quot;&lt;/strong&gt; attribute to script and style elements.&lt;/p&gt;
&lt;p&gt;If you want to see what this looks like in practice,&amp;nbsp;&lt;a href=&quot;https://www.linkedin.com/in/minesh-shah-dev/&quot;&gt;Minesh&lt;/a&gt;&amp;nbsp;has written an excellent walkthrough of building exactly this middleware in Next.js for the PaaS version of Stott Security:&amp;nbsp;&lt;a href=&quot;/link/27bc4af218b148caac6aab1e14871b00.aspx&quot;&gt;Dynamic CSP Management for Headless and Hybrid Optimizely CMS with Next.js&lt;/a&gt;. Because the header structure is identical, the same middleware approach carries straight over to the SaaS product. The only difference is the envelope: the PaaS API returns a bare array while the SaaS endpoint wraps the array in an object, so a one-line check such as &lt;strong&gt;Array.isArray(body) ? body : body.headers&lt;/strong&gt; allows a single middleware implementation to serve both products.&lt;/p&gt;
&lt;h2&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;Stott Security for Optimizely SaaS CMS is in early access, and this is exactly the right time to influence its direction. I would love to hear how you would plug this into your own SaaS headless solution: which framework does your head run on, would the planned middleware package be useful to you, and which of the missing features matter most to your builds?&lt;/p&gt;
&lt;p&gt;Like the PaaS add-on, Stott Security for SaaS is free and open source under the MIT licence. If you would like to try it against your own Optimizely SaaS CMS instance then&amp;nbsp;&lt;a href=&quot;https://github.com/GeekInTheNorth/Stott.Security.Optimizely.SaaS/discussions/1&quot;&gt;join the Discussion&lt;/a&gt;. If you want to help shape what comes next, please&amp;nbsp;&lt;a href=&quot;https://github.com/GeekInTheNorth/Stott.Security.Optimizely.SaaS&quot;&gt;raise an issue on the Stott Security for SaaS GitHub repository&lt;/a&gt; &amp;mdash; feature requests, integration questions and early access requests are all welcome there.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I&#39;m a Platinum Optimizely MVP and the maintainer of two open source add-ons: Stott Security for &lt;a href=&quot;https://github.com/GeekInTheNorth/Stott.Security.Optimizely.SaaS&quot;&gt;SaaS CMS&lt;/a&gt; and &lt;a href=&quot;https://github.com/GeekInTheNorth/Stott.Security.Optimizely&quot;&gt;PaaS CMS 12 &amp;amp; 13&lt;/a&gt;, and &lt;a href=&quot;https://github.com/GeekInTheNorth/Stott.Optimizely.RobotsHandler&quot;&gt;Stott Robots Handler&lt;/a&gt; for PaaS CMS 12 &amp;amp; 13. All of my writing is collated at &lt;a href=&quot;https://www.stott.pro/&quot;&gt;https://www.stott.pro&lt;/a&gt;.&lt;/p&gt;</id><updated>2026-08-13T13:29:29.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely XML Resource Builder: A Visual Studio Extension for Language Resource Files</title><link href="https://www.adnanzameer.com/2026/08/optimizely-xml-resource-builder.html" /><id>&lt;!--excerpt: Optimizely XML Resource Builder is a Visual Studio extension that generates Optimizely language resource XML from content type definitions, helping teams keep editor labels, descriptions, and shared properties in sync with less manual work.--&gt;

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,</id><updated>2026-08-13T09:34:42.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Optimizely SaaS CMS Management API: The Write Side of Headless</title><link href="https://kpbasics.com/?p=8413" /><id>Everything I&amp;#8217;ve written in this series so far has been about reading content — Optimizely Graph, webhooks, ISR, caching layers. But at some point you need to actually write content programmatically. Maybe you&amp;#8217;re migrating 500 blog posts from WordPress. Maybe you want a seed script that spins up demo content on every fresh CMS instance. [&amp;#8230;]</id><updated>2026-08-12T21:22:01.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>